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

嵌套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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 22:03:23