在适配器 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 areItemsTheSame 和 areContentsTheSame 的 callback,但我不清楚这意味着什么。我正在添加一些代码,请看一下。如果我做错了,请指导我。
组键
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. 如果我们比较 int、boolean 或 String,我们使用 == 还是别的什么?
在这个适配器内,我正在调用另一个 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
而不是 notifyDataSetChanged
。 notifyDataSetChanged
会导致对所有视图进行毫无意义的刷新,并破坏使用 ListAdapter 和 DiffUtil 的目的。
【讨论】:
再次感谢一百万✌️。很棒 嘿@Tenfour04 我需要你的帮助。我正在更新适配器中的项目,更新 reyclerview 中的项目时它不起作用。我添加了示例。 example以上是关于在适配器 kotlin 中更新视图时如何正确使用 Diff utils的主要内容,如果未能解决你的问题,请参考以下文章