嵌套RecyclerView折叠重展开后notify不触发onBindViewHolder问题
嵌套RecyclerView折叠后展开notify不触发onBindViewHolder问题修复
核心根因
- 折叠逻辑仅修改子RecyclerView的
visibility为GONE,未清理子RecyclerView的视图缓存,再次展开时直接复用旧的离屏ViewHolder,跳过绑定流程 - 父Adapter在
onBindViewHolder中重复创建/设置子Adapter、LayoutManager实例,导致业务侧持有的子Adapter引用和子RecyclerView实际绑定的Adapter实例不一致,调用notify方法时事件无法派发到正确的视图 - 折叠状态下子RecyclerView未接入视图树,此时调用notify系列方法触发的刷新事件会被丢弃,后续展开时没有补发刷新动作,UI保留旧状态
- 嵌套场景下子RecyclerView开启
setHasFixedSize(true),导致自身尺寸变化时不会触发重新测量和布局
落地修复步骤
- 子Adapter、LayoutManager仅在父ViewHolder初始化时创建一次,禁止在
onBindViewHolder中重复设置
// 父Adapter对应ViewHolder实现 class CdlParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val subRecyclerView: RecyclerView = itemView.findViewById(R.id.cdl_sub_rv) // 子Adapter全局初始化一次,和ViewHolder生命周期绑定 val subAdapter: CdlSubAdapter = CdlSubAdapter() init { // LayoutManager也仅初始化设置一次 subRecyclerView.layoutManager = LinearLayoutManager(itemView.context) subRecyclerView.adapter = subAdapter // 嵌套场景关闭固定尺寸标记,避免测量失效 subRecyclerView.setHasFixedSize(false) // 父条目点击折叠/展开逻辑 itemView.setOnClickListener { val pos = bindingAdapterPosition if (pos == RecyclerView.NO_POSITION) return@setOnClickListener val itemData = currentList[pos] val willExpand = !itemData.isExpanded itemData.isExpanded = willExpand if (willExpand) { // 展开动作:显示视图+清理缓存+主动触发刷新 subRecyclerView.visibility = View.VISIBLE subRecyclerView.recycledViewPool.clear() subAdapter.notifyDataSetChanged() subRecyclerView.requestLayout() } else { // 折叠动作:直接隐藏即可 subRecyclerView.visibility = View.GONE } notifyItemChanged(pos) } } fun bindData(parentItem: CdlParentItem) { // 绑定父条目自身数据即可,不要在这里重复设置子RV的adapter/layoutManager // 子列表数据直接传给已初始化的subAdapter subAdapter.submitList(parentItem.scanGoodsList) subRecyclerView.visibility = if (parentItem.isExpanded) View.VISIBLE else View.GONE } }
- 子条目交互逻辑统一修改数据源后再触发刷新,不要直接操作ViewHolder视图
// CdlSubAdapter中的子条目点击逻辑 inner class SubViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { itemView.setOnClickListener { val pos = bindingAdapterPosition if (pos == RecyclerView.NO_POSITION) return@setOnClickListener // 第一步:先修改对应数据模型的状态 currentList[pos].apply { showErrorTip = false showEditDeleteBtn = true } // 第二步:再触发局部刷新 notifyItemChanged(pos) } } }
- 折叠状态下如果有子列表数据更新,不要提前调用notify,等展开动作触发时统一刷新即可,避免刷新事件发送到未挂载的RecyclerView上被丢弃。
校验点
- 打印子RecyclerView绑定的adapter实例和业务侧持有的subAdapter实例,确认二者是同一对象,不存在重复创建替换的问题
- 多次折叠展开后点击子条目,日志确认
onBindViewHolder正常回调,视图状态和数据模型保持一致 - 快速折叠展开连续操作,不存在子列表错位、数据不刷新的问题
内容的提问来源于stack exchange,提问作者Thedarlin
相关产品推荐
相关产品推荐

