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

RecyclerView嵌套Adapter更新后滚动位置异常问题求助

嵌套RecyclerView加载更多后滚动位置异常的解决方案

场景:垂直RecyclerView(Adapter1)的每个条目包含水平RecyclerView(Adapter2),滚动Adapter2到底部触发loadMore,下载数据后调用Adapter1.notifyDataSetChanged(),再调用水平RecyclerView的scrollTo(),但滚动无效,最终回到第一项。已确认数据和滚动位置正确,且受后端API限制无法单独更新Adapter2条目。

问题根源

  1. notifyDataSetChanged的异步特性:RecyclerView收到更新通知后,布局重建是异步执行的,直接在notifyDataSetChanged()后调用scrollTo()时,水平RecyclerView还未完成新Adapter2的绑定或布局更新,导致滚动指令失效。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 00:53:31