使用FirestoreRecyclerAdapter时getItemCount()始终返回0的解决方法
问题:FirestoreRecyclerAdapter数据存在时仍弹出无数据对话框
我正在开发一款洗衣设备预订应用,需要在条目数量为0时向用户显示无数据提示对话框。但目前即使RecyclerView中有数据,对话框仍会弹出,请问该如何修复?
Activity代码
public class DobbySelection2 extends AppCompatActivity { String local; private Dialog dialog; private FirebaseFirestore db = FirebaseFirestore.getInstance(); private DobbyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dobby_selection2); dialog = new Dialog(this); dialog.setContentView(R.layout.custom_dialog2); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { dialog.getWindow().setBackgroundDrawable(getDrawable(R.drawable.custom_dialogbackground)); } dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); dialog.setCancelable(false); //Optional dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation; //Setting the animations to dialog Button Yes = dialog.findViewById(R.id.btn_yes); Yes.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(DobbySelection2.this, MainActivity.class ); dialog.dismiss(); startActivity(intent); } }); setUpRecyclerView(); } private void setUpRecyclerView(){ Intent i = getIntent(); local = i.getStringExtra("PLACE"); if (local == null){ local = "Selangor"; } CollectionReference dobbyRef = db.collection("locality") .document(local) .collection("Dobby"); Query query = dobbyRef.orderBy("name", Query.Direction.DESCENDING); FirestoreRecyclerOptions<Dobby> options = new FirestoreRecyclerOptions.Builder<Dobby>() .setQuery(query, Dobby.class) .build(); adapter = new DobbyAdapter(options); RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); //recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setLayoutManager(new CustomLinearLayoutManager(this)); recyclerView.setAdapter(adapter); if(adapter.getItemCount() == 0){ dialog.show(); } adapter.setOnItemClickListener(new DobbyAdapter.OnItemClickListener() { @Override public void onItemClick(DocumentSnapshot documentSnapshot, int position) { Dobby dobby = documentSnapshot.toObject(Dobby.class); String id = documentSnapshot.getId(); Toast.makeText(DobbySelection2.this, "ID : " + id, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(DobbySelection2.this, Booking2.class); intent.putExtra("PLACE", local); intent.putExtra("ID", id); startActivity(intent); } }); } @Override protected void onStart() { super.onStart(); adapter.startListening(); } @Override protected void onStop() { super.onStop(); adapter.stopListening(); } }
Adapter代码
public class DobbyAdapter extends FirestoreRecyclerAdapter<Dobby, DobbyAdapter.DobbyHolder>{ private OnItemClickListener listener; /** * Create a new RecyclerView adapter that listens to a Firestore Query. See {@link * FirestoreRecyclerOptions} for configuration options. * * @param options */ public DobbyAdapter(@NonNull FirestoreRecyclerOptions<Dobby> options) { super(options); } @Override protected void onBindViewHolder(@NonNull DobbyHolder holder, int position, @NonNull Dobby model) { holder.textViewName.setText(model.getName()); holder.textViewAddress.setText(model.getAddress()); holder.textViewDistance.setText(model.getDistance()); } @NonNull @Override public DobbyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemdobby, parent, false); return new DobbyHolder(v); } class DobbyHolder extends RecyclerView.ViewHolder{ TextView textViewName; TextView textViewAddress; TextView textViewDistance; public DobbyHolder(@NonNull View itemView) { super(itemView); textViewName = itemView.findViewById(R.id.nameDobby); textViewAddress = itemView.findViewById(R.id.addressDobby); textViewDistance = itemView.findViewById(R.id.distanceDobby); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); if(position != RecyclerView.NO_POSITION && listener != null){ listener.onItemClick(getSnapshots().getSnapshot(position), position); } } }); } } public interface OnItemClickListener { void onItemClick(DocumentSnapshot documentSnapshot, int position); } public void setOnItemClickListener(OnItemClickListener listener){ this.listener = listener; } }
解决方案
问题根源
FirestoreRecyclerAdapter的getItemCount()在初始化阶段会返回0,因为Firestore的数据是异步加载的。你在setUpRecyclerView()中立即调用adapter.getItemCount()时,数据还未从云端拉取完成,因此这个判断永远会触发对话框弹出,和实际数据是否存在无关。
修复步骤
我们需要利用FirestoreRecyclerAdapter提供的onDataChanged()方法——这个方法会在数据首次加载完成、或者数据发生变化时被调用,此时可以准确获取实际的条目数量。
1. 修改Adapter,添加数据加载回调
在Adapter中新增一个回调接口,用于通知Activity数据加载完成,并在onDataChanged()中触发该回调:
public class DobbyAdapter extends FirestoreRecyclerAdapter<Dobby, DobbyAdapter.DobbyHolder>{ private OnItemClickListener listener; private OnDataLoadedListener dataLoadedListener; // 新增数据加载回调 public DobbyAdapter(@NonNull FirestoreRecyclerOptions<Dobby> options) { super(options); } // 新增设置回调的方法 public void setOnDataLoadedListener(OnDataLoadedListener listener) { this.dataLoadedListener = listener; } @Override protected void onBindViewHolder(@NonNull DobbyHolder holder, int position, @NonNull Dobby model) { holder.textViewName.setText(model.getName()); holder.textViewAddress.setText(model.getAddress()); holder.textViewDistance.setText(model.getDistance()); } @NonNull @Override public DobbyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemdobby, parent, false); return new DobbyHolder(v); } // 重写onDataChanged,数据加载/变化时触发 @Override public void onDataChanged() { super.onDataChanged(); if (dataLoadedListener != null) { dataLoadedListener.onDataLoaded(getItemCount()); } } class DobbyHolder extends RecyclerView.ViewHolder{ TextView textViewName; TextView textViewAddress; TextView textViewDistance; public DobbyHolder(@NonNull View itemView) { super(itemView); textViewName = itemView.findViewById(R.id.nameDobby); textViewAddress = itemView.findViewById(R.id.addressDobby); textViewDistance = itemView.findViewById(R.id.distanceDobby); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); if(position != RecyclerView.NO_POSITION && listener != null){ listener.onItemClick(getSnapshots().getSnapshot(position), position); } } }); } } public interface OnItemClickListener { void onItemClick(DocumentSnapshot documentSnapshot, int position); } public void setOnItemClickListener(OnItemClickListener listener){ this.listener = listener; } // 新增数据加载完成的回调接口 public interface OnDataLoadedListener { void onDataLoaded(int itemCount); } }
2. 修改Activity,监听数据加载状态
移除setUpRecyclerView()中原来的if(adapter.getItemCount() == 0)判断,改为通过Adapter的回调来处理对话框显示:
private void setUpRecyclerView(){ Intent i = getIntent(); local = i.getStringExtra("PLACE"); if (local == null){ local = "Selangor"; } CollectionReference dobbyRef = db.collection("locality") .document(local) .collection("Dobby"); Query query = dobbyRef.orderBy("name", Query.Direction.DESCENDING); FirestoreRecyclerOptions<Dobby> options = new FirestoreRecyclerOptions.Builder<Dobby>() .setQuery(query, Dobby.class) .build(); adapter = new DobbyAdapter(options); RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new CustomLinearLayoutManager(this)); recyclerView.setAdapter(adapter); // 移除原来的错误判断逻辑 // if(adapter.getItemCount() == 0){ // dialog.show(); // } // 添加数据加载完成监听 adapter.setOnDataLoadedListener(new DobbyAdapter.OnDataLoadedListener() { @Override public void onDataLoaded(int itemCount) { if (itemCount == 0) { if (!dialog.isShowing()) { dialog.show(); } } else { if (dialog.isShowing()) { dialog.dismiss(); } } } }); adapter.setOnItemClickListener(new DobbyAdapter.OnItemClickListener() { @Override public void onItemClick(DocumentSnapshot documentSnapshot, int position) { Dobby dobby = documentSnapshot.toObject(Dobby.class); String id = documentSnapshot.getId(); Toast.makeText(DobbySelection2.this, "ID : " + id, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(DobbySelection2.this, Booking2.class); intent.putExtra("PLACE", local); intent.putExtra("ID", id); startActivity(intent); } }); }
效果说明
修改后,只有当Firestore数据加载完成且确实没有数据时,才会弹出无数据对话框。如果数据加载成功且有内容,对话框会自动关闭(如果之前因异步加载误弹的话),同时还能处理数据动态变化的场景(比如数据被全部删除后,对话框会重新弹出)。
内容的提问来源于stack exchange,提问作者Haziq Mt Roslan
相关产品推荐
相关产品推荐

