RecyclerView嵌套Adapter更新后滚动位置异常问题求助
嵌套RecyclerView加载更多后滚动位置异常的解决方案
场景:垂直RecyclerView(Adapter1)的每个条目包含水平RecyclerView(Adapter2),滚动Adapter2到底部触发loadMore,下载数据后调用Adapter1.notifyDataSetChanged(),再调用水平RecyclerView的scrollTo(),但滚动无效,最终回到第一项。已确认数据和滚动位置正确,且受后端API限制无法单独更新Adapter2条目。
问题根源
notifyDataSetChanged的异步特性:RecyclerView收到更新通知后,布局重建是异步执行的,直接在notifyDataSetChanged()后调用scrollTo()时,水平RecyclerView还未完成新Adapter2的绑定或布局更新,导致滚动指令失效。- Adapter2未复用:Adapter1的
onBindViewHolder每次新建Adapter2实例,notify后原Adapter2被替换,滚动状态完全丢失,最终水平RecyclerView默认回到第一项。
无需修改API的解决方案
方案1:延迟滚动到布局完成后执行
利用View.post()将滚动操作推迟到RecyclerView完成布局更新后执行,确保滚动时新的Adapter2已经绑定并完成布局:
handler.post(() -> { Adapter1.notifyDataSetChanged(); // 用post延迟执行,保证RecyclerView已完成布局重建 Adapter1.RecyclerViewForAdapter2Item.post(() -> { Adapter1.RecyclerViewForAdapter2Item.scrollToPosition(position); }); });
方案2:复用Adapter2实例,保留滚动状态
修改Adapter1,缓存每个位置的Adapter2实例,避免每次onBind都新建,从根源上保留滚动状态:
class Adapter1 extends RecyclerView.Adapter<Adapter1.ViewHolder> { // 用SparseArray缓存对应位置的Adapter2,适配int类型的位置索引 private SparseArray<Adapter2> adapter2Cache = new SparseArray<>(); private List<ParentData> dataList; @Override public void onBindViewHolder(ViewHolder holder, int position) { ParentData item = dataList.get(position); Adapter2 adapter2 = adapter2Cache.get(position); // 仅在缓存不存在时新建Adapter2 if (adapter2 == null) { adapter2 = new Adapter2(item.getInitialData()); adapter2Cache.put(position, adapter2); } // 更新Adapter2的数据源(需给Adapter2添加setData方法) adapter2.setData(item.getUpdatedData()); holder.horizontalRecyclerView.setAdapter(adapter2); } // 当整体数据结构变化时清空缓存,按需调整 public void refreshData(List<ParentData> newData) { this.dataList = newData; adapter2Cache.clear(); notifyDataSetChanged(); } }
配合方案1的延迟滚动,滚动状态的保留会更稳定。
方案3:精准获取目标水平RecyclerView
确保滚动操作针对当前显示的目标条目对应的水平RecyclerView,避免引用失效:
// 先记录触发loadMore的垂直列表位置和水平滚动目标位置 int targetVerticalPos = arg1; // 假设arg1是Adapter1中的条目位置 int targetHorizontalPos = position; handler.post(() -> { Adapter1.notifyDataSetChanged(); RecyclerView verticalRV = getView().findViewById(R.id.vertical_recycler_view); RecyclerView.LayoutManager layoutManager = verticalRV.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; // 获取目标位置的ViewHolder Adapter1.ViewHolder targetHolder = (Adapter1.ViewHolder) verticalRV.findViewHolderForAdapterPosition(targetVerticalPos); if (targetHolder != null) { RecyclerView horizontalRV = targetHolder.horizontalRecyclerView; horizontalRV.post(() -> { horizontalRV.scrollToPosition(targetHorizontalPos); }); } } });
总结
不需要调整后端API,通过前端代码优化即可解决问题:优先复用Adapter2实例保留滚动状态,再配合延迟滚动的方式,就能避免scrollTo提前执行导致的位置异常问题。
内容的提问来源于stack exchange,提问作者danyapd
相关产品推荐
相关产品推荐

