在适配器 kotlin 中更新视图时如何正确使用 Diff utils

Posted

技术标签:

【中文标题】在适配器 kotlin 中更新视图时如何正确使用 Diff utils【英文标题】:How to proper way of using Diff utils when updating view in adapter kotlin 【发布时间】:2021-12-08 12:10:24 【问题描述】:

嘿,我是 adpater 中 DiffUtil 的新手。我从堆栈溢出、谷歌文档和一些文章中阅读了一些文章。我正在尝试理解 DiffUtil areItemsTheSameareContentsTheSamecallback,但我不清楚这意味着什么。我正在添加一些代码,请看一下。如果我做错了,请指导我。

组键

data class GroupKey(
    val type: EnumType,
    val sender: Sender? = null,
    val close: String? = null
)

枚举类型

enum class EnumType 
    A,
    B

发件人

data class Sender(
    val company: RoleType? = null,
    val id: String? = null
)

角色类型

data class RoleType(
    val name : String?= null
    val id: String? = null
)

data class Group(
    val key: GroupKey,
    val value: MutableList<Item?>
)

我将我的列表传递给适配器,它是一个 Group mutableList

var messageGroupList: MutableList<Group>? = null
..
val adapter = MainAdapter()
binding.recylerview.adapter = adapter
adapter.submitList(groupList)

在适配器中使用 DiffUtil

MainAdapter.kt

class MainAdapter :ListAdapter<Group, RecyclerView.ViewHolder>(COMPARATOR) 

    companion object 
        private val COMPARATOR = object : DiffUtil.ItemCallback<Group>() 
            override fun areItemsTheSame(oldItem: Group, newItem: Group): Boolean 
                return oldItem == newItem
            

            override fun areContentsTheSame(oldItem: Group, newItem: Group): Boolean 
                return ((oldItem.value == newItem.value) && (oldItem.key == newItem.key))
            
        
    
.....

1. 我需要在此 DiffUtil.ItemCallback 中比较 key 其他属性,如 type、sender 等。 p>

2. 何时使用 ===== 以及如何使用 equals()

3. 如果我们比较 intbooleanString,我们使用 == 还是别的什么?

在这个适配器内,我正在调用另一个 Recyclerview,并在该适配器内传递 Item 列表。

物品

data class Item(
    val text: String? = null,
    var isRead: Boolean? = null,
    val sender: Sender? = null,
    val id: Int? = null
)

NestedRecyclerView.kt

class NestedRecyclerView : ListAdapter<Item, IncomingMessagesViewHolder>(COMPARATOR) 

    companion object 
        private val COMPARATOR = object : DiffUtil.ItemCallback<Item>() 
            override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean 
                return oldItem.id == newItem.id
            

            override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean 
                return ((oldItem.isRead == oldItem.isRead) &&
                        (oldItem.sender == newItem.sender) &&
                        (oldItem.text == oldItem.text))
            
        
    

同样的问题我是否也需要在这里比较 sender's 的其他属性。

4.areItemsTheSame 中我需要比较 id 还是只比较 oldItem == newItem 这个?

5. 如何正确更新我的适配器项目。在普通的 reyclerview 中,我们使用 notifiyDataSetChanged。但是在 diffutil 中我是否需要再次调用 submitList 函数并且它会处理所有事情?

adapter.submitList(groupList)

【问题讨论】:

【参考方案1】:

问题 1 和 4:

areItemsTheSame 表示两个实例代表同一个数据项,即使某些内容可能不同。假设您有一个联系人列表,并且 Jane 的中间名首字母已更改,但该行仍应代表同一个人 Jane。您的模型类可能有不同的实例,具有一些不同的值,但它们应该代表同一行。

因此,在这种情况下,您通常只会比较旧项目和新项目之间相同的一件事。通常,如果您从数据库或 API 中获取数据,将会有一些唯一的 ID 代表一个数据点,这就是您需要在 areItemsTheSame 中进行比较的全部内容。例如,oldItem.id == newItem.id

areContentsTheSame 表示如果两个实例都显示在您的列表中,它们看起来会相同。因此,如果您使用data class,则使用oldItem == newItem 就足够了,因为数据类有一个equals 函数来比较每个属性。

在您的 Item 回调代码中,您的 areItemsTheSame 看起来是正确的,但您的 areContentsTheSame 过于复杂。由于Item是一个数据类,你只需要直接比较两个item即可。

override fun areContentsTheSame(oldItem: Item, newItem: Item) = oldItem == newItem

在您的 Group 回调代码中,也许您可​​以比较旧项目和新项目的 GroupKey,如果这是确定项目相同的有效方法。由于您仅使用直接的== 比较,因此当项目部分更改时,您可能会出现一些视觉缺陷,例如视图消失和重新出现,而不是简单地更改部分文本。

问题 2 您应该很少在 Kotlin 中使用 ===。它不仅检查两个项目是否相等,而且检查这两个项目是否引用内存中完全相同的实例。它根本不适合 DiffUtil.ItemCallback。

问题 3 == 是比较任意两个对象的正确方法。在 Kotlin 中,即使是基元也应该以这种方式进行比较,因为它们的行为类似于对象。

问题 5 使用 ListAdapter,您应该始终使用 submitList 而不是 notifyDataSetChangednotifyDataSetChanged 会导致对所有视图进行毫无意义的刷新,并破坏使用 ListAdapter 和 DiffUtil 的目的。

【讨论】:

再次感谢一百万✌️。很棒 嘿@Tenfour04 我需要你的帮助。我正在更新适配器中的项目,更新 reyclerview 中的项目时它不起作用。我添加了示例。 example

以上是关于在适配器 kotlin 中更新视图时如何正确使用 Diff utils的主要内容,如果未能解决你的问题,请参考以下文章

删除项目后如何更新列表视图?

在使用数据绑定时如何设置嵌套的回收器视图适配器?

如何在kotlin的片段内显示网格视图?

Kotlin:获取点击项目列表视图的值(片段+适配器)

Kotlin入门(23)适配器的进阶表达

如何在列表适配器中正确使用 ViewHolder 和自定义视图