嵌套RecyclerView平滑滚动到position 0时部分元素不显示
问题原因
这个异常是嵌套RecyclerView回收复用逻辑冲突导致的:
- 你为每个内嵌RecyclerView单独创建了独立的
RecycledViewPool,同时开启了recycleChildrenOnDetach = true。父RV执行smoothScrollToPosition(0)快速滚动时,离屏的父项会被回收,对应子RV的子View会被存入子RV自己的独立回收池;当父项被重新复用绑定到屏幕上时,独立回收池和新绑定的子RV实例没有正确关联,子View无法被取出重新布局。 - 你给子RV设置了
isNestedScrollingEnabled = false,子RV不会参与父RV的嵌套滚动布局协调,复用时不会主动触发重算;如果子RV高度是随内容自适应的,setHasFixedSize(true)还会让系统跳过measure步骤,直接导致子RV区域空白,同项其他非RV内容因为走了正常的bind和布局流程所以可以正常显示。 - 如果你在父项的
onBindViewHolder里每次都重新new子RV的LayoutManager和Adapter,也会导致子RV的布局状态在快速滚动复用时丢失。
修复步骤
- 移除子RV单独创建独立回收池的逻辑,所有同类型的子RV共享父RV持有的同一个回收池,只在父RV初始化时创建一次即可:
// 父RV初始化时执行一次 val sharedChildPool = RecyclerView.RecycledViewPool() parentRecyclerView.setRecycledViewPool(sharedChildPool) // 子RV绑定时直接传入共享池,不要new新的RecycledViewPool childRecyclerView.setRecycledViewPool(sharedChildPool)
- 子RV的LayoutManager和Adapter不要在每次
onBindViewHolder时重复创建,放到父项ViewHolder的构造函数里初始化一次,避免复用时状态丢失:
class ParentItemViewHolder(itemView: View, onChildItemClick: OnChildItemClick) : RecyclerView.ViewHolder(itemView) { private val childRv = itemView.findViewById<RecyclerView>(R.id.child_recycler_view) // 构造时一次性初始化LayoutManager private val childLayoutManager = GridLayoutManager(itemView.context, 2).apply { recycleChildrenOnDetach = true } private val childAdapter = ChildAdapter(onChildItemClick) init { childRv.layoutManager = childLayoutManager childRv.adapter = childAdapter childRv.isNestedScrollingEnabled = false // 子RV高度如果是wrap_content自适应,必须删掉setHasFixedSize(true)这行 // childRv.setHasFixedSize(true) } fun bind(data: DatesModelObject) { childAdapter.oldList = data.list childAdapter.notifyDataSetChanged() // 绑定数据后主动触发布局请求,修正复用时的空白问题 childRv.requestLayout() } }
- 如果快速滚动后还是偶现空白,可以给父RV加滚动状态监听,滚动停止后给当前屏幕可见的子RV主动触发一次布局刷新:
parentRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { val lm = recyclerView.layoutManager as LinearLayoutManager for (i in lm.findFirstVisibleItemPosition()..lm.findLastVisibleItemPosition()) { val holder = recyclerView.findViewHolderForAdapterPosition(i) as? ParentItemViewHolder ?: continue holder.itemView.findViewById<RecyclerView>(R.id.child_recycler_view)?.requestLayout() } } } })
内容的提问来源于stack exchange,提问作者Chuy Porras
相关产品推荐
相关产品推荐

