RecyclerView嵌套CardView结合Room使用初始不渲染需滚动后显示问题咨询
问题根因
- 每次
onBindViewHolder执行时都重复创建子适配器、布局管理器,导致子RecyclerView需要重复渲染,初始加载时父布局无法正确测量高度 - LiveData异步返回子分类数据后,仅更新了子适配器,没有触发父RecyclerView重新测量当前项的尺寸,导致父项没有预留足够的展示空间,滚动后父布局重新测量才正常显示
- ViewModel中
getItemListByCat方法每次调用都会覆盖itemsForCat成员变量,多个父项同时查询时会出现数据错乱 - 观察者绑定的是页面级生命周期,重复绑定会导致内存泄漏、旧数据回调覆盖新数据的问题
修复方案
1. 修正ViewModel逻辑
移除itemsForCat成员变量,每次查询直接返回独立的LiveData实例,避免多个查询互相覆盖:
public class SubCategoryModel extends AndroidViewModel { private SubCategoryRepository mySubCategoryRepository; private LiveData<List<sub_category>> allItems; public SubCategoryModel(Application application) { super(application); mySubCategoryRepository = new SubCategoryRepository(application); allItems = mySubCategoryRepository.getAllItems(); } public LiveData<List<sub_category>> getAllItems() { return allItems; } // 移除成员变量存储,每次直接返回新的查询结果 public LiveData<List<sub_category>> getItemListByCat(Integer catid) { return mySubCategoryRepository.getItemListByCat(catid); } }
同理Repository中的itemsListByCat也可以改成局部变量,不需要作为成员变量存储。
2. 优化父Adapter绑定逻辑
将子RecyclerView的初始化逻辑移到onCreateViewHolder中,只执行一次,避免重复创建:
// 父Adapter构造方法中初始化ViewModel,不要每次onBind都获取 public ParentAdapter(Context context, Fragment fragment, List<category> categoryItems) { this.myFcontext = fragment; this.myCategoryItems = categoryItems; this.mySubCategoryModel = new ViewModelProvider(myFcontext).get(SubCategoryModel.class); } @Override public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.parent_item, parent, false); ItemViewHolder holder = new ItemViewHolder(view); // 子RecyclerView配置仅初始化一次 holder.subRecyclerView.setLayoutManager(new LinearLayoutManager(parent.getContext(), RecyclerView.HORIZONTAL, false)); holder.subRecyclerView.setHasFixedSize(true); // 子适配器仅初始化一次 holder.subAdapter = new SubCategoryCardAdapter(parent.getContext(), myFcontext); holder.subRecyclerView.setAdapter(holder.subAdapter); return holder; } @Override public void onBindViewHolder(ItemViewHolder holder, int position) { category current = myCategoryItems.get(position); holder.textViewName.setText(current.Category); // 先给子适配器设置空占位,避免复用时显示旧数据 holder.subAdapter.setItems(Collections.emptyList()); // 观察当前分类的子数据,用holder作为观察者的生命周期Owner,避免重复绑定 mySubCategoryModel.getItemListByCat(current.cat_id).observe(holder, sub_categoryList -> { holder.subAdapter.setItems(sub_categoryList); // 关键:更新数据后触发父项重新测量尺寸 holder.itemView.requestLayout(); }); } // ViewHolder类实现LifecycleOwner接口,管理单个Item的观察生命周期 static class ItemViewHolder extends RecyclerView.ViewHolder implements LifecycleOwner { TextView textViewName; RecyclerView subRecyclerView; SubCategoryCardAdapter subAdapter; private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this); public ItemViewHolder(View itemView) { super(itemView); textViewName = itemView.findViewById(R.id.category_name); subRecyclerView = itemView.findViewById(R.id.sub_recycler); } @Override public Lifecycle getLifecycle() { return lifecycleRegistry; } // 绑定的时候标记生命周期为STARTED void bind() { lifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); } // 回收的时候标记生命周期为DESTROYED,自动移除观察者 void recycle() { lifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED); } } // 父Adapter中重写对应方法,管理ViewHolder生命周期 @Override public void onViewRecycled(@NonNull ItemViewHolder holder) { super.onViewRecycled(holder); holder.recycle(); } @Override public void onBindViewHolder(@NonNull ItemViewHolder holder, int position, @NonNull List<Object> payloads) { super.onBindViewHolder(holder, position, payloads); holder.bind(); }
3. 可选优化
给子RecyclerView设置最小高度,或者在父Item布局中给子RecyclerView预留默认高度,避免初始加载时高度为0导致的视觉闪烁。
内容的提问来源于stack exchange,提问作者James Harvey
相关产品推荐
相关产品推荐

