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

RecyclerView使用DiffUtil修改Item的isChecked属性后UI未即时更新问题

问题根因

你遇到的刷新异常核心是DiffUtil对比时无法识别到内容变化,本质是两个错误用法导致:

  • 你直接修改了原有列表中Item对象的checked属性,没有创建新的Item实例。ListAdapter持有的旧列表和你新提交的列表共用同一个Item对象引用,DiffUtil对比时拿到的旧对象属性已经被修改,自然判断新旧内容一致,不会触发条目刷新。只有当条目滚动出屏幕再重新进入时,才会重新绑定数据拿到最新的checked值。
  • 嵌套列表的Diff判断直接用了引用相等判断,如果你修改嵌套列表内容时没有创建新的列表实例,也会导致DiffUtil认为条目没有变化。
解决方案

1. 修改属性时必须创建新的Item实例

禁止直接修改原有列表中的Item属性,所有属性变更都要基于原对象创建新的副本。

方案A(推荐):将Java的Item类改为Kotlin数据类

Kotlin数据类默认实现了equals、hashCode和copy方法,修改属性非常方便:

data class Item(
    val count: Int? = null,
    val id: Int? = null,
    val icon_img: String? = null,
    val name: String? = null,
    val cover_img: String? = null,
    val group_name: String? = null,
    val parent_id: Int? = null,
    val status: Int? = null,
    val checked: Boolean = false,
    val whatToVisible: Int? = null
)

修改checked属性的逻辑改为:

private fun addOrRemoveItemsFromPopularItems(id: String) {
    val oldPopularItems = _popularItemsLiveData.value?.data ?: return
    // 创建新的列表副本,不修改原列表
    val newPopularItems = oldPopularItems.map { item ->
        if (item.id.toString() == id) {
            // 拷贝原有属性,仅修改checked值
            item.copy(checked = !item.checked)
        } else {
            item
        }
    }
    _popularItemsLiveData.postValue(Success(newPopularItems))
}

方案B:保留Java类时新增拷贝构造方法

给Java的Item类新增拷贝构造方法,修改属性时用该方法创建新实例:

// Item类中新增
public Item(Item origin) {
    this.count = origin.count;
    this.id = origin.id;
    this.icon_img = origin.icon_img;
    this.name = origin.name;
    this.cover_img = origin.cover_img;
    this.group_name = origin.group_name;
    this.parent_id = origin.parent_id;
    this.status = origin.status;
    this.checked = origin.checked;
    this.whatToVisible = origin.whatToVisible;
}

修改属性时的逻辑:

private fun addOrRemoveItemsFromPopularItems(id: String) {
    val oldPopularItems = _popularItemsLiveData.value?.data ?: return
    val newPopularItems = oldPopularItems.map { item ->
        if (item.id.toString() == id) {
            val newItem = Item(item)
            newItem.setChecked(!item.isChecked())
            newItem
        } else {
            item
        }
    }
    _popularItemsLiveData.postValue(Success(newPopularItems))
}

2. 修正DiffUtil中嵌套列表的判断逻辑

不要用引用相等判断嵌套列表的条目一致性,优先用嵌套列表的唯一标识(如类型、分组ID)判断是否是同一个条目:

private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<MainDataClass>() {
    override fun areItemsTheSame(
        oldItem: MainDataClass,
        newItem: MainDataClass
    ): Boolean {
        if (oldItem is MainDataClass.Item && newItem is MainDataClass.Item) {
            return oldItem.data.id == newItem.data.id
        } else if (oldItem is MainDataClass.List && newItem is MainDataClass.List) {
            // 替换为你嵌套列表对应的唯一标识判断,比如分组ID、列表类型
            return oldItem.groupId == newItem.groupId
            // 无唯一标识时可以用列表长度+首元素ID粗略判断:
            // return oldItem.list.size == newItem.list.size && oldItem.list.firstOrNull()?.id == newItem.list.firstOrNull()?.id
        } 
        return false
    }

    override fun areContentsTheSame(
        oldItem: MainDataClass,
        newItem: MainDataClass
    ): Boolean {
        if (oldItem is MainDataClass.Item && newItem is MainDataClass.Item) {
            return oldItem.data == newItem.data
        } else if (oldItem is MainDataClass.List && newItem is MainDataClass.List) {
            // 这里用Kotlin的结构相等(对应Java的List.equals),只要嵌套列表元素的equals逻辑正确就能正常对比
            return oldItem.list == newItem.list
        }
        return false
    }
}

3. 所有列表修改都要创建新的列表实例

禁止直接修改LiveData中存储的原有列表实例,所有修改都要先创建列表副本:

// 错误:直接修改原有列表
var itemList = _selectedItems.value ?: ArrayList()
// 正确:创建新的列表副本
var itemList = _selectedItems.value?.toMutableList() ?: ArrayList()

内容的提问来源于stack exchange,提问作者Aziz Agasi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 10:27:05