当只有伴随对象发生变化时,DiffUtil 如何知道列表变化?
How can DiffUtil know about list changes when only the companion object has changed?
如图,我想把Detail item
的单位按toggle button
一下子换一下。
Detail list items
被设置为 companion objects
因为确定没有必要单独设置一个 unit
属性.
但是,DiffUtil
似乎确定new list
和old list
之间没有变化,可能是因为unit
属性是设置为 companion object
.
所以视图也没有更新。
如何在更改伴随对象时使 DiffUtil 响应?
详情
@Entity
data class Detail(
@PrimaryKey(autoGenerate = true)
var id: Int,
val set: Int,
var weight: String = "",
var reps: String = "") {
companion object {
var title: String = ""
var unit: String = "kg"
val memo = ""
}
}
ViewModel
class DetailViewModel(application: Application) : ViewModel() {
private val repository: DetailRepository
private val _items: MutableLiveData<List<Detail>> = MutableLiveData()
val items = _items
private val list: List<Detail>
get() = _items.value ?: emptyList()
init {
val detailDao = DetailDatabase.getDatabase(application)!!.detailDao()
repository = DetailRepository(detailDao)
}
fun changeUnit(unit: String) {
Detail.unit = unit
if(list == null)
return
_items.postValue(list) // To notify the observer.
}
fun addDetail() {
viewModelScope.launch(Dispatchers.IO){
val item = Detail(0, set = list.size+1)
repository.add(item)
// If use plus(), a new List is returned.
// Therefore, the change is notified to the Observer by postValue of the new list added.
_items.postValue(list.plus(item))
}
}
fun deleteDetail() {
// Delete the last set and return a new list to postValue to notify the Observer of the change.
_items.postValue(list.dropLast(1))
}
}
DiffUtil
class DetailDiffCallback : DiffUtil.ItemCallback<Detail>() {
override fun areItemsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return oldItem == newItem
}
}
片段
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList)
}
}
Detail list items were set as companion objects because it was determined that it was not necessary to have a unit property individually.
这就是问题的根源。如果您希望 DiffUtil
能够“看到”这些更改,则必须将此信息移出伴随对象。
DiffUtil
通过获取 class 的两个实例并执行工作(areItemsTheSame()
和 areContentsTheSame()
方法)来查看是否有任何更改。由于此信息是伴随对象的一部分,因此对于所有实例而言它始终是相同的,这意味着 DiffUtil
无法检测到更改,即使发生了更改也是如此。
正如 Ben P 所说,当您更改伴随对象中的值时,会影响整个 class(因为它们都共享该对象)。它不包含在为数据 class 生成的 equals()
代码中 - 因为为什么需要它?每个实例共享相同的值,它不是状态的一部分!
即使是,当您比较 oldList
和 newList
时,oldList
仍然被“更新”为新的 unit
值,因为它在那共享伴随对象。如果您希望 oldList
和 newList
能够为 unit
设置不同的值,它们需要是 per-instance 属性。
但是你在这里做事的方式,unit
似乎甚至不是数据的一部分,对吧?这不像是在存储一个值和一个度量单位,并在显示的单位类型发生变化时进行转换。 看起来这只是一个通用显示选项,仅适用于数据的显示方式。
那么在那种情况下,为什么不直接在适配器上调用 notifyDataSetChanged()
或类似的东西呢?强制重绘,让它显示新的单位类型,就是这样。如果您使用 DiffUtil
,我不确定您是否需要做任何特别的事情,但这就是我要研究的内容。
(我觉得将单位类型存储在数据中会是一种更好的方法,对于存储的数字实际上 意味着 似乎很重要,但对于您正在做的事情现在,显示刷新应该就足够了)
如图,我想把Detail item
的单位按toggle button
一下子换一下。
Detail list items
被设置为 companion objects
因为确定没有必要单独设置一个 unit
属性.
但是,DiffUtil
似乎确定new list
和old list
之间没有变化,可能是因为unit
属性是设置为 companion object
.
所以视图也没有更新。
如何在更改伴随对象时使 DiffUtil 响应?
详情
@Entity
data class Detail(
@PrimaryKey(autoGenerate = true)
var id: Int,
val set: Int,
var weight: String = "",
var reps: String = "") {
companion object {
var title: String = ""
var unit: String = "kg"
val memo = ""
}
}
ViewModel
class DetailViewModel(application: Application) : ViewModel() {
private val repository: DetailRepository
private val _items: MutableLiveData<List<Detail>> = MutableLiveData()
val items = _items
private val list: List<Detail>
get() = _items.value ?: emptyList()
init {
val detailDao = DetailDatabase.getDatabase(application)!!.detailDao()
repository = DetailRepository(detailDao)
}
fun changeUnit(unit: String) {
Detail.unit = unit
if(list == null)
return
_items.postValue(list) // To notify the observer.
}
fun addDetail() {
viewModelScope.launch(Dispatchers.IO){
val item = Detail(0, set = list.size+1)
repository.add(item)
// If use plus(), a new List is returned.
// Therefore, the change is notified to the Observer by postValue of the new list added.
_items.postValue(list.plus(item))
}
}
fun deleteDetail() {
// Delete the last set and return a new list to postValue to notify the Observer of the change.
_items.postValue(list.dropLast(1))
}
}
DiffUtil
class DetailDiffCallback : DiffUtil.ItemCallback<Detail>() {
override fun areItemsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(
oldItem: Detail,
newItem: Detail
): Boolean {
return oldItem == newItem
}
}
片段
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList)
}
}
Detail list items were set as companion objects because it was determined that it was not necessary to have a unit property individually.
这就是问题的根源。如果您希望 DiffUtil
能够“看到”这些更改,则必须将此信息移出伴随对象。
DiffUtil
通过获取 class 的两个实例并执行工作(areItemsTheSame()
和 areContentsTheSame()
方法)来查看是否有任何更改。由于此信息是伴随对象的一部分,因此对于所有实例而言它始终是相同的,这意味着 DiffUtil
无法检测到更改,即使发生了更改也是如此。
正如 Ben P 所说,当您更改伴随对象中的值时,会影响整个 class(因为它们都共享该对象)。它不包含在为数据 class 生成的 equals()
代码中 - 因为为什么需要它?每个实例共享相同的值,它不是状态的一部分!
即使是,当您比较 oldList
和 newList
时,oldList
仍然被“更新”为新的 unit
值,因为它在那共享伴随对象。如果您希望 oldList
和 newList
能够为 unit
设置不同的值,它们需要是 per-instance 属性。
但是你在这里做事的方式,unit
似乎甚至不是数据的一部分,对吧?这不像是在存储一个值和一个度量单位,并在显示的单位类型发生变化时进行转换。 看起来这只是一个通用显示选项,仅适用于数据的显示方式。
那么在那种情况下,为什么不直接在适配器上调用 notifyDataSetChanged()
或类似的东西呢?强制重绘,让它显示新的单位类型,就是这样。如果您使用 DiffUtil
,我不确定您是否需要做任何特别的事情,但这就是我要研究的内容。
(我觉得将单位类型存储在数据中会是一种更好的方法,对于存储的数字实际上 意味着 似乎很重要,但对于您正在做的事情现在,显示刷新应该就足够了)