Kotlin嵌套RecyclerView中DiffUtil失效 仅notifyDataSetChanged可刷新
嵌套RecyclerView DiffUtil失效问题解决方案
核心问题原因
- 复用了已提交的可变数据实例
你在addData方法中直接修改原有Group内部的可变列表,再把同一个列表实例赋值给groupList。同时groupByData方法也复用了原有groupList实例,DiffUtil对比新旧列表时发现是同个内存地址的对象,会直接判定无变化,跳过差异计算。 - 列表拷贝仅为浅拷贝
你提交列表时调用的toMutableList()仅拷贝了外层Group列表的结构,内层每个Group实例、Group内部的子列表都是原实例的引用,DiffUtil对比内容时会判定内容无变化。 - 多余的全量刷新调用
你在submitList后主动调用了notifyDataSetChanged(),直接覆盖了DiffUtil的差异更新逻辑,强制全量刷新视图。 - 未贴出的代码大概率存在的问题:DiffUtil.ItemCallback实现错误,或者子RecyclerView的Adapter未正确提交新数据。
修复步骤
1. 所有数据类改为不可变实现
将Group、GroupKey、Abc、Qwe均定义为Kotlin数据类,内部的列表使用不可变的List而非MutableList,禁止修改已提交给Adapter的旧数据实例。
// 示例Group数据类定义 data class Group( val key: GroupKey, val list: List<Abc> // 用不可变List )
2. 数据更新时生成全新的实例
修改addData方法,所有修改都基于新实例生成,不修改原有数据:
fun addData() { groupList?.let { oldList -> val qwe = Qwe("Vivek ${value++}", "Modi") val newItem = Abc(type = "Type 1", "Adding Message", qwe) // 生成新的外层列表实例 val newList = ArrayDeque<Group>() newList.addAll(oldList) // 生成更新后的第一个Group实例 val oldFirstGroup = newList.removeFirst() val newFirstGroup = oldFirstGroup.copy( list = oldFirstGroup.list + newItem ) newList.addFirst(newFirstGroup) // 赋值新的列表实例触发更新 groupList = newList } }
3. 正确实现DiffUtil.ItemCallback
外层GroupAdapter的DiffCallback要基于内容对比,不要用引用对比:
val GROUP_DIFF_CALLBACK = object : DiffUtil.ItemCallback<Group>() { override fun areItemsTheSame(oldItem: Group, newItem: Group): Boolean { // 替换为你Group的唯一标识字段,比如GroupKey内的唯一ID return oldItem.key.uniqueId == newItem.key.uniqueId } override fun areContentsTheSame(oldItem: Group, newItem: Group): Boolean { // Kotlin数据类默认equals会对比所有字段,包括内部List的内容 return oldItem == newItem } }
子RecyclerView对应的子Adapter也要实现对应的DiffUtil.ItemCallback,外层绑定子Item时,用子Adapter的submitList提交新的子列表。
4. 删除多余的全量刷新代码
移除Observer内的notifyDataSetChanged调用:
private fun setupViewModel() { viewModel.groupListLiveData.observe(this) { if (groupAdapter == null) { groupAdapter = GroupAdapter(GROUP_DIFF_CALLBACK) // 传入正确的DiffCallback binding.recyclerview.adapter = groupAdapter } groupAdapter?.submitList(viewModel.groupList?.toList()) // 删掉下面两行全量刷新代码 // binding.recyclerview.post { // groupAdapter?.notifyDataSetChanged() // } } }
完成以上修改后DiffUtil即可正常计算差异,无需调用全量刷新方法即可更新嵌套RecyclerView的内容。
内容的提问来源于stack exchange,提问作者Compose Learner
相关产品推荐
相关产品推荐

