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

嵌套RecyclerView含多视图类型首次滚动卡顿掉帧问题如何解决?

卡顿原因及修复方案

1. 禁止在ViewHolder的bind方法中重复初始化子RecyclerView

你当前每次绑定LottoPrizeOtherListHolder时,都会重新创建GridLayoutManager、重新给子RecyclerView设置adapter,这会导致每次条目滚动到可视区域时都要重新走子列表的初始化、测量逻辑,是卡顿的核心原因。
修改方案:把子RecyclerView的初始化逻辑移到ViewHolder的构造方法中,bind方法只更新子列表的数据:

class LottoPrizeOtherListHolder(
    private val binding: ItemLottoPrizesOtherListBinding,
    private val lottoLatestPrizeOtherAdapter: LottoLatestPrizeOtherAdapter,
    viewPool: RecyclerView.RecycledViewPool
) : RecyclerView.ViewHolder(binding.root) {

    // 构造方法中一次性初始化子RecyclerView
    init {
        binding.prizeOtherRecyclerView.apply {
            layoutManager = GridLayoutManager(context,2,GridLayoutManager.VERTICAL,false)
            adapter = lottoLatestPrizeOtherAdapter
            setRecycledViewPool(viewPool)
            // 根据单屏展示的子项数量调整固定预取数量,不要每次bind都修改
            initialPrefetchItemCount = 4 
            // 禁用子RecyclerView的嵌套滚动,避免滑动冲突和多余的事件处理
            isNestedScrollingEnabled = false
        }
    }

    fun bind(lottoLatestPrizeOther: LottoPrizesLatestModel.LottoLatestPrizeOther) = with(binding) {
        // bind方法仅做数据更新
        lottoLatestPrizeOtherAdapter.numberList = lottoLatestPrizeOther.prizeModel.number.toMutableList()
        // 建议子Adapter也接入DiffUtil做局部刷新,替代全量notifyDataSetChanged
        lottoLatestPrizeOtherAdapter.notifyDataSetChanged()
        titlePrizeOtherTextView.text = lottoLatestPrizeOther.prizeModel.name
        textPrizeOneNearTextView.text = itemView.context.getString(
            R.string.lotto_prize_per_baht,
            lottoLatestPrizeOther.prizeModel.reward
        )
    }
}

对应父Adapter的onCreateViewHolder创建该Holder的时候传入共享的viewPool即可,同时必须删除onBindViewHolder里的viewPool.putRecycledView(holder)代码,这段是完全错误的调用,会破坏父RecyclerView本身的回收复用逻辑。

2. 优化子RecyclerView的测量性能

你当前子RecyclerView的高度设置为wrap_content,会导致每次更新数据时都要测量所有子项的高度,可根据业务场景选择两种优化方案:

  • 如果每个子条目的高度固定,给子RecyclerView的子项设置固定高度,同时给子LayoutManager开启setHasFixedSize(true)
  • 如果子列表的总高度不高,直接把父条目里的RecyclerView替换成GridView/FlowLayout等静态布局,避免嵌套RecyclerView的额外开销

3. 替换全量刷新为局部刷新

你当前给latestList赋值时调用notifyDataSetChanged()全量刷新,会导致所有可见条目都重新绑定,效率极低。接入DiffUtil实现局部刷新:

var latestList: MutableList<LottoPrizesLatestModel> = mutableListOf()
    set(value) {
        val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize() = field.size
            override fun getNewListSize() = value.size
            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                // 替换为你的条目唯一标识判断逻辑
                return field[oldItemPosition].id == value[newItemPosition].id
            }
            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                // 替换为你的条目内容相等判断逻辑
                return field[oldItemPosition] == value[newItemPosition]
            }
        })
        field = value
        diffResult.dispatchUpdatesTo(this)
    }

4. 开启父RecyclerView的预取优化

给父RecyclerView的LinearLayoutManager开启预取:

lottoRecyclerView.apply {
    layoutManager = LinearLayoutManager(context).apply {
        // 开启嵌套RecyclerView的预取
        isItemPrefetchEnabled = true
        // 调整预取数量,根据你单屏展示的父条目数量+2即可
        prefetchDistance = 3
    }
    adapter = lottoLatestAdapter
    // 如果父条目的高度固定,开启这个可以避免不必要的布局重算
    setHasFixedSize(true)
}

做完以上修改后首次滚动的卡顿问题会完全消失。


内容的提问来源于stack exchange,提问作者Yothin Homjan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 22:06:02