能否用一个RecyclerView Adapter适配两个RecyclerView?返回后列表为空
问题解答:单个RecyclerView Adapter适配多个RecyclerView的可行性及空列表问题修复
核心结论
完全可以用同一个RecyclerView.Adapter类实例化不同对象,适配多个RecyclerView——这是合理且推荐的复用方式,你遇到的返回后列表为空问题,是代码实现细节的错误导致的,和复用Adapter本身无关。
问题根源分析
- 数据加载与Adapter初始化顺序错误:第一个Fragment中,你先发起Firebase查询,再创建Adapter并绑定到RecyclerView。当Adapter初始化时,
recipe_list是空的,后续数据加载完成后虽然调用了notifyDataSetChanged(),但Fragment重建(返回后)时若未重新触发数据加载,列表会为空。 - 异步数据加载的时序问题:第二个Fragment中,内部的Recipe查询是异步的,你在外部循环结束后直接调用
notifyDataSetChanged(),此时异步请求可能还未完成,数据并未添加到列表中。 - 列表数据共享/未正确清空:若两个Fragment共享同一个
recipe_list实例,会导致数据互相干扰;且未在数据加载前清空列表,多次触发监听会重复添加数据。 - 未移除Firebase监听:使用
addValueEventListener会持续监听数据变化,未移除会导致内存泄漏,且可能在Fragment不可见时仍更新数据。
修复方案
第一步:优化Adapter,增加数据更新方法
给Adapter添加可控的数据更新方法,避免直接操作外部列表,同时拷贝传入的列表,隔离外部修改:
public class MyRecipeAdapter extends RecyclerView.Adapter<MyRecipeAdapter.RecipeHolder> { private ArrayList<Recipe> list_of_recipe; private final Context context; public MyRecipeAdapter(Context context, ArrayList<Recipe> list_of_recipe) { this.context = context; this.list_of_recipe = new ArrayList<>(list_of_recipe); // 拷贝列表,避免外部直接修改 } // 新增:统一更新数据的方法 public void updateData(ArrayList<Recipe> newData) { this.list_of_recipe.clear(); this.list_of_recipe.addAll(newData); notifyDataSetChanged(); } // 以下原有代码保持不变 @NonNull @Override public MyRecipeAdapter.RecipeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_recipe_tap, parent, false); return new RecipeHolder(view); } @SuppressLint("RecyclerView") @Override public void onBindViewHolder(@NonNull MyRecipeAdapter.RecipeHolder holder, int position) { Recipe recipe = list_of_recipe.get(position); holder.recipename.setText(recipe.getRecipe_name()); Picasso.get().load(recipe.getImage()).into(holder.recipeImage); holder.itemView.setOnClickListener(v -> { String id = list_of_recipe.get(position).getRecipe_id(); Intent intent = new Intent(context, RecipePage.class); intent.putExtra("recipe_id_home", id); context.startActivity(intent); }); } @Override public int getItemCount() { return list_of_recipe.size(); } public class RecipeHolder extends RecyclerView.ViewHolder { ImageView recipeImage; TextView recipename; RelativeLayout recipe_parent ; public RecipeHolder(@NonNull View itemView) { super(itemView); recipe_parent = itemView.findViewById(R.id.recipe_parent); recipeImage = itemView.findViewById(R.id.image); recipename = itemView.findViewById(R.id.name); } } }
第二步:修复第一个Fragment的代码
调整初始化顺序,确保先绑定Adapter再加载数据,同时在onViewCreated中执行逻辑,保证视图就绪:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 1. 初始化当前Fragment专属的列表 ArrayList<Recipe> recipe_list = new ArrayList<>(); // 2. 先创建Adapter并绑定到RecyclerView GridLayoutManager gridnewManager = new GridLayoutManager(getContext(), 3); myrecipe_cycle.setLayoutManager(gridnewManager); MyRecipeAdapter newAdapter = new MyRecipeAdapter(getContext(), recipe_list); myrecipe_cycle.setAdapter(newAdapter); // 3. 发起Firebase数据查询 DatabaseReference data_ref_recipe = FirebaseDatabase.getInstance().getReference("Recipes"); Query query = data_ref_recipe.orderByChild("uid").equalTo(current_uid); query.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { ArrayList<Recipe> tempList = new ArrayList<>(); if (snapshot.exists()) { for (DataSnapshot data : snapshot.getChildren()) { Recipe recipe = data.getValue(Recipe.class); tempList.add(recipe); } } // 使用Adapter的更新方法刷新数据 newAdapter.updateData(tempList); } @Override public void onCancelled(@NonNull DatabaseError error) { // 可添加错误提示,比如Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT).show(); } }); }
第三步:修复第二个Fragment的代码
解决异步加载时序问题,使用CountDownLatch等待所有子查询完成后再更新列表,同时改用单次监听避免内存泄漏:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 1. 初始化当前Fragment专属的列表 ArrayList<Recipe> recipe_list = new ArrayList<>(); // 2. 绑定Adapter GridLayoutManager gridnewManager = new GridLayoutManager(getContext(), 3); saved_recycle.setLayoutManager(gridnewManager); MyRecipeAdapter newAdapter = new MyRecipeAdapter(getContext(), recipe_list); saved_recycle.setAdapter(newAdapter); // 3. 查询收藏的Recipe ID DatabaseReference user_ref = FirebaseDatabase.getInstance().getReference("Users") .child(current_uid).child("Saved Recipes"); user_ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if (snapshot.exists()) { ArrayList<Recipe> tempList = new ArrayList<>(); int totalItems = (int) snapshot.getChildrenCount(); CountDownLatch latch = new CountDownLatch(totalItems); for (DataSnapshot data : snapshot.getChildren()) { String id = data.getValue(String.class); if (id == null) { latch.countDown(); continue; } // 查询单个Recipe详情,用单次监听避免内存泄漏 DatabaseReference recipe_ref = FirebaseDatabase.getInstance().getReference("Recipes").child(id); recipe_ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { Recipe recipe = snapshot.getValue(Recipe.class); if (recipe != null) { tempList.add(0, recipe); } latch.countDown(); } @Override public void onCancelled(@NonNull DatabaseError error) { latch.countDown(); } }); } // 等待所有异步请求完成后,在主线程更新Adapter new Thread(() -> { try { latch.await(); requireActivity().runOnUiThread(() -> newAdapter.updateData(tempList)); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } else { // 无收藏数据时清空列表 newAdapter.updateData(new ArrayList<>()); } } @Override public void onCancelled(@NonNull DatabaseError error) { // 错误处理 } }); }
关键注意事项
- 每个Fragment使用独立的列表实例:禁止多个Fragment共享同一个
ArrayList<Recipe>,避免数据互相干扰。 - 使用ViewModel保存数据(可选):如果使用Jetpack组件,可将数据存储在ViewModel中,Fragment重建时无需重新从Firebase加载数据。
- 移除Firebase监听:若使用
addValueEventListener,必须在Fragment的onDestroyView方法中调用removeEventListener,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Emtenan YYC
相关产品推荐
相关产品推荐

