如何正确使用回调接口从onDataChange获取RecyclerView的值列表? [英] How to use callback interface correctly to get a list of values from onDataChange for RecyclerView?

查看:51
本文介绍了如何正确使用回调接口从onDataChange获取RecyclerView的值列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从Firebase数据库中获取某个数据库的子级,并将其值放入 ArrayList< MyClass> 中,以供在 Recyclerview.Adapter 中使用,但是在完成之后 onDataChange 方法中的数据附加过程的列表,我的列表等于null.

我知道它是由于onDataChange的异步行为而发生的.感谢此答案

我的回调界面:

  import java.util.List;公共接口FirebaseCallback {void onCallback(List< Sentinel> list);} 

我的单例班:

 公共类SentinelStorage {//单例类公共静态SentinelStorage sentinelStorage;私有列表< Sentinel>sentinelsList;...公共列表< Sentinel>readSentinelsListFromDB(DatabaseReference dbRef,final FirebaseCallback firebaseCallback){最终Sentinel哨兵= new Sentinel();ValueEventListener valueEventListener = new ValueEventListener(){@Override公共无效onDataChange(DataSnapshot dataSnapshot){GenericTypeIndicator< Map< String,Object>>t = new GenericTypeIndicator< Map< String,Object>>(){};Map< String,Object>sentinelsMap = dataSnapshot.getValue(t);用于(Map.Entry< String,Object>条目:sentinelsMap.entrySet()){//将Map的每个节点转换为Sentinel实例sentinelsList.add(sentinel.mapToSentinel((Map)entry.getValue()));}firebaseCallback.onCallback(sentinelsList);}@Override公共无效onCancelled(DatabaseError databaseError){Log.d(TAG,databaseError.getMessage());}};dbRef.addListenerForSingleValueEvent(valueEventListener);返回sentinelsList;//这里的sentinelsList仍然不为空}...} 

SentinelActivity类:

 公共类ActSentinel扩展BaseActivity实现FirebaseCallback {私有静态最终String TAG =#ActSentinel";RecyclerView mRecyclerView;SentinelViewAdapter适配器;私有最终字符串DBSentintelName ="sentinel";前哨哨兵= new Sentinel();FirebaseDatabase mFirebaseDatabase = FirebaseDatabase.getInstance();DatabaseReference dbRef = mFirebaseDatabase.getReference(DBSentintelName);List< Sentinel>list = new ArrayList<>();SentinelStorage sentinelStorage;@Override公共无效onCallback(List< Sentinel> lst){list.addAll(lst);adapter.notifyDataSetChanged();Log.d(TAG,"INSIDE onCallback列表为空::" + list.isEmpty());}@Override受保护的void onCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.sentinel_view);mRecyclerView =(RecyclerView)findViewById(R.id.recyclerView);mRecyclerView.setLayoutManager(new LinearLayoutManager(this));适配器=新的SentinelViewAdapter(列表);sentinelStorage = SentinelStorage.get();//sentinelStorage.loadSentinelsListFromDB(dbRef);//使用非空列表进行测试sentinelStorage.readSentinelsListFromDB(dbRef,this);mRecyclerView.setAdapter(adapter);Log.d(TAG,"!!!! list empty :: :: + list.isEmpty());}公共类SentinelViewHolder扩展了RecyclerView.ViewHolder {前哨哨兵;TextView tvLogin;TextView tvPassword;TextView tvName;TextView tvSurname;SentinelViewHolder(查看视图){超级(视图);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);}公共无效绑定(前哨哨兵){this.sentinel =前哨;tvLogin.setText(sentinel.login);tvPassword.setText(sentinel.password);tvName.setText(sentinel.name);tvSurname.setText(sentinel.surname);}}公共类SentinelViewAdapter扩展了RecyclerView.Adapter< SentinelViewHolder> {List< Sentinel>mSentinels;公共SentinelViewAdapter(List< Sentinel>警卫队){mSentinels =守卫;}公共SentinelViewHolder onCreateViewHolder(ViewGroup container,int viewType){视图view = LayoutInflater.from(container.getContext()).inflate(R.layout.item_of_sentinel_table,container,false);SentinelViewHolder vh =新的SentinelViewHolder(view);返回vh;}public void onBindViewHolder(SentinelViewHolder sentinelVH,int position){前哨哨兵= mSentinels.get(position);sentinelVH.bind(sentinel);}public int getItemCount(){返回mSentinels.size();}}} 

解决方案

您的列表已传递到适配器,您要做的就是通知适配器您的数据集已更改.同样不要在回调中更改它的引用(将另一个列表分配给变量 list ),只需调用.addAll()即可.这样,您的适配器已经知道您的列表,只需通知它即可.

  list = sentinelStorage.readSentinelsListFromDB(dbRef,new FirebaseCallback(){@Override公共无效onCallback(List< Sentinel> lst){list.addAll(lst);adapter.notifyDatasetChanged();Log.d(TAG,"onCallback.is list empty" + list.isEmpty());//不为空}}); 

此外,您无需使readSentinelsListFromDB返回一个列表,因为该列表已在回调中返回.所以我也将其删除.

所以这样调用您的方法:

  sentinelStorage.readSentinelsListFromDB(dbRef,new FirebaseCallback(){@Override公共无效onCallback(List< Sentinel> lst){list.addAll(lst);adapter.notifyDatasetChanged();Log.d(TAG,"onCallback.is list empty" + list.isEmpty());//不为空}}); 

要使代码更整洁,您可以做的另一件事是使活动实现该接口:

 公共类ActivitySentinel扩展BaseActivity实现FirebaseCallback 

这将迫使您在活动中创建一个方法,您将在其中接收回叫:

  @Override公共无效onCallback(List< Sentinel> lst){list.addAll(lst);adapter.notifyDatasetChanged();Log.d(TAG,"onCallback.is list empty" + list.isEmpty());//不为空} 

这样,您的方法调用将更改为:

  sentinelStorage.readSentinelsListFromDB(dbRef,this); 

I'm trying to get some database' child from Firebase database and put its values into ArrayList<MyClass> for using in Recyclerview.Adapter, but after the completion of data appending process in onDataChange method my list equals null.

I understand that it happens because of asynchronous behavior of onDataChange. Thanks to this answer How to return dataSnapshot value as a result of a method? I've created my FirebaseCallback interface and yes I do get my arrayList.

But I get it only inside its onCallback method. However, I need to pass the arrayList outside of that method to RecyclerView.Adapter.

Do I have to rebuild my RecylerView.Adapter and create that one inside the onCallback method? Please, can anybody explain me how to figure out my problem? Thanks a lot!

About my code:

I have a SentintelActivity that has to show the list of Sentinel using the RecyclerView. Sentinel is a class of entity that i fetch from FirebaseDatabase. Another class SentinelStorage is a singleton with main field List and a method readSentinelsListFromDB which has to return my list of Sentinels. At the moment that list equals null. In addition to code, I've attached a schema for more clearly understanding.

My callback interface:

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

My singleton class:

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 class:

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();
        }
    }
}

解决方案

Your list has been passed to the adapter already, all you have to do is notify the adapter that something has changed in your datase. Also don't change the reference of it in the callback (assign another list to the variable list), simply call .addAll(). That way your adapter already knows about your list, it will simply need to be notified.

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
        }
    });

Moreover you don't need to make readSentinelsListFromDB return a List as it is already returned in the callback. so I'd remove that as well.

So call your method like this:

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
        }
    });

Another thing you could do to make your code cleaner is make your activity implement that interface:

public class ActivitySentinel extends BaseActivity implements FirebaseCallback

This will force you to create a method in your activity where you'll receive your callback calls:

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

This way your method call would change to:

sentinelStorage.readSentinelsListFromDB(dbRef,this);

这篇关于如何正确使用回调接口从onDataChange获取RecyclerView的值列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆