当只有伴随对象发生变化时,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 listold 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() 代码中 - 因为为什么需要它?每个实例共享相同的值,它不是状态的一部分!

即使是,当您比较 oldListnewList 时,oldList 仍然被“更新”为新的 unit 值,因为它在那共享伴随对象。如果您希望 oldListnewList 能够为 unit 设置不同的值,它们需要是 per-instance 属性。


但是你在这里做事的方式,unit 似乎甚至不是数据的一部分,对吧?这不像是在存储一个值和一个度量单位,并在显示的单位类型发生变化时进行转换。 看起来这只是一个通用显示选项,仅适用于数据的显示方式。

那么在那种情况下,为什么不直接在适配器上调用 notifyDataSetChanged() 或类似的东西呢?强制重绘,让它显示新的单位类型,就是这样。如果您使用 DiffUtil,我不确定您是否需要做任何特别的事情,但这就是我要研究的内容。

(我觉得将单位类型存储在数据中会是一种更好的方法,对于存储的数字实际上 意味着 似乎很重要,但对于您正在做的事情现在,显示刷新应该就足够了)