Как правильно использовать интерфейс обратного вызова, чтобы получить список значений из onDataChange для RecyclerView? - PullRequest
0 голосов
/ 26 апреля 2018

Я пытаюсь получить дочернюю базу данных из базы данных Firebase и поместить ее значения в ArrayList<MyClass> для использования в Recyclerview.Adapter, но после завершения процесса добавления данных в методе onDataChange мой список равен нулю.

Я понимаю, что это происходит из-за асинхронного поведения onDataChange. Благодаря этому ответу Как вернуть значение dataSnapshot в результате метода? Я создал свой интерфейс FirebaseCallback и да, я получаю свой arrayList.

Но я получаю его только внутри метода onCallback. Однако мне нужно передать arrayList вне этого метода в RecyclerView.Adapter.

Нужно ли перестраивать мой RecylerView.Adapter и создавать его внутри метода onCallback? Пожалуйста, кто-нибудь может объяснить мне, как понять мою проблему? Большое спасибо!

О моем коде:

У меня есть SentintelActivity, которое должно отображать список Sentinel с помощью RecyclerView. Sentinel - это класс сущностей, который я извлекаю из FirebaseDatabase. Другой класс SentinelStorage - это синглтон с основным полем List и методом readSentinelsListFromDB, который должен возвращать мой список Sentinels. На данный момент этот список равен нулю. В дополнение к коду я приложил схему для более ясного понимания. enter image description here

Мой интерфейс обратного вызова:

import java.util.List;
public interface FirebaseCallback {
    void onCallback (List<Sentinel> list);
}

Мой синглтон-класс:

public class SentinelStorage { // Singleton class
    public static SentinelStorage sentinelStorage;
    private List<Sentinel> sentinelsList;

    ...
    public List<Sentinel> readSentinelsListFromDB(DatabaseReference dbRef, final FirebaseCallback firebaseCallback){
        final Sentinel sentinel = new Sentinel();
        ValueEventListener valueEventListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                GenericTypeIndicator<Map<String, Object>> t = new GenericTypeIndicator<Map<String, Object>>(){};
                Map<String, Object> sentinelsMap = dataSnapshot.getValue(t);
                for (Map.Entry<String,Object> entry : sentinelsMap.entrySet()){
                    // convert every node of Map to Sentinel instance
                    sentinelsList.add(sentinel.mapToSentinel((Map)entry.getValue()));
                }
                firebaseCallback.onCallback(sentinelsList);
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.d(TAG,databaseError.getMessage());
            }
        };
        dbRef.addListenerForSingleValueEvent(valueEventListener);
        return sentinelsList; // here sentinelsList still is not empty
    }
    ...
}

SentinelActivity класс:

public class ActSentinel extends BaseActivity implements FirebaseCallback{
    private static final String TAG = "# ActSentinel";

    RecyclerView mRecyclerView;
    SentinelViewAdapter adapter;
    private final String DBSentintelName = "sentinel";

    Sentinel sentinel = new Sentinel();
    FirebaseDatabase mFirebaseDatabase = FirebaseDatabase.getInstance();
    DatabaseReference dbRef = mFirebaseDatabase.getReference(DBSentintelName);
    List<Sentinel> list = new ArrayList<>();
    SentinelStorage sentinelStorage;

    @Override
    public void onCallback(List<Sentinel> lst) {
        list.addAll(lst);
        adapter.notifyDataSetChanged();
        Log.d(TAG, "INSIDE onCallback list empty :: "+list.isEmpty());
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sentinel_view);
        mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        adapter = new SentinelViewAdapter(list);

        sentinelStorage = SentinelStorage.get();
        //sentinelStorage.loadSentinelsListFromDB(dbRef);// testing with notnull list
        sentinelStorage.readSentinelsListFromDB(dbRef, this);

        mRecyclerView.setAdapter(adapter);

        Log.d(TAG, "!!!! list empty :: "+list.isEmpty());
    }

    public class SentinelViewHolder extends RecyclerView.ViewHolder{
        Sentinel sentinel;
        TextView tvLogin;
        TextView tvPassword;
        TextView tvName;
        TextView tvSurname;

        SentinelViewHolder(View view){
            super(view);
            tvLogin = (TextView) view.findViewById(R.id.login_value);
            tvPassword = (TextView) view.findViewById(R.id.password_value);
            tvName = (TextView) view.findViewById(R.id.name);
            tvSurname = (TextView) view.findViewById(R.id.surname);
        }

        public void bind (Sentinel sentinel){
            this.sentinel = sentinel;
            tvLogin.setText(sentinel.login);
            tvPassword.setText(sentinel.password);
            tvName.setText(sentinel.name);
            tvSurname.setText(sentinel.surname);
        }
    }

    public class SentinelViewAdapter extends RecyclerView.Adapter<SentinelViewHolder>{
        List<Sentinel> mSentinels;

        public SentinelViewAdapter(List<Sentinel> guards){
            mSentinels = guards;
        }


        public SentinelViewHolder onCreateViewHolder(ViewGroup container, int viewType){
            View view = LayoutInflater.from(container.getContext())
                    .inflate(R.layout.item_of_sentinel_table, container,false);                
            SentinelViewHolder vh = new SentinelViewHolder(view);
            return vh;
        }


        public void onBindViewHolder(SentinelViewHolder sentinelVH, int position){
            Sentinel sentinel = mSentinels.get(position);
            sentinelVH.bind(sentinel);
        }

        public int getItemCount(){
            return mSentinels.size();
        }
    }
}

1 Ответ

0 голосов
/ 26 апреля 2018

Ваш список уже передан адаптеру, все, что вам нужно сделать, это уведомить адаптер, что что-то изменилось в вашей базе данных. Также не меняйте ссылку на него в обратном вызове (назначьте другой список переменной list), просто вызовите .addAll (). Таким образом, ваш адаптер уже знает о вашем списке, его просто нужно будет уведомить.

list = sentinelStorage.readSentinelsListFromDB(dbRef, new FirebaseCallback() {
        @Override
        public void onCallback(List<Sentinel> lst) {
            list.addAll(lst);
            adapter.notifyDatasetChanged();
            Log.d(TAG, "onCallback.Is list empty "+list.isEmpty());// not EMPTY
        }
    });

Более того, вам не нужно заставлять readSentinelsListFromDB возвращать List, поскольку он уже возвращен в обратном вызове. так что я бы тоже это убрал.

Итак, назовите ваш метод так:

sentinelStorage.readSentinelsListFromDB(dbRef, new FirebaseCallback() {
        @Override
        public void onCallback(List<Sentinel> lst) {
            list.addAll(lst);
            adapter.notifyDatasetChanged();
            Log.d(TAG, "onCallback.Is list empty "+list.isEmpty());// not EMPTY
        }
    });

Еще одна вещь, которую вы можете сделать, чтобы сделать свой код чище, это заставить вашу деятельность реализовать этот интерфейс:

public class ActivitySentinel extends BaseActivity implements FirebaseCallback

Это заставит вас создать метод в вашей деятельности, где вы будете получать обратные вызовы:

@Override
public void onCallback(List<Sentinel> lst) {
   list.addAll(lst);
   adapter.notifyDatasetChanged();
   Log.d(TAG, "onCallback.Is list empty "+list.isEmpty());// not EMPTY
}

Таким образом, ваш вызов метода изменится на:

sentinelStorage.readSentinelsListFromDB(dbRef,this);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...