You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用一个RecyclerView Adapter适配两个RecyclerView?返回后列表为空

问题解答:单个RecyclerView Adapter适配多个RecyclerView的可行性及空列表问题修复

核心结论

完全可以用同一个RecyclerView.Adapter类实例化不同对象,适配多个RecyclerView——这是合理且推荐的复用方式,你遇到的返回后列表为空问题,是代码实现细节的错误导致的,和复用Adapter本身无关。


问题根源分析

  1. 数据加载与Adapter初始化顺序错误:第一个Fragment中,你先发起Firebase查询,再创建Adapter并绑定到RecyclerView。当Adapter初始化时,recipe_list是空的,后续数据加载完成后虽然调用了notifyDataSetChanged(),但Fragment重建(返回后)时若未重新触发数据加载,列表会为空。
  2. 异步数据加载的时序问题:第二个Fragment中,内部的Recipe查询是异步的,你在外部循环结束后直接调用notifyDataSetChanged(),此时异步请求可能还未完成,数据并未添加到列表中。
  3. 列表数据共享/未正确清空:若两个Fragment共享同一个recipe_list实例,会导致数据互相干扰;且未在数据加载前清空列表,多次触发监听会重复添加数据。
  4. 未移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:25:14