带有 DiffUtil.ItemCallback 的 ListAdapter 始终认为对象相同

ill*_*zor 12 android android-recyclerview

我正在尝试将 ListAdapter 与 Room 和 LifeData 一起使用。但是我遇到了 DiffUtil.ItemCallback 的奇怪行为 - areContentsTheSame() 方法中的对象总是相同的。添加和删​​除对象没问题,但更改内容有问题。

物品类别:

@Entity(tableName = "item")
data class Item(var num: Int) {

    @PrimaryKey(autoGenerate = true)
    var key: Int = 0

}
Run Code Online (Sandbox Code Playgroud)

适配器类

class LifeAdapter : ListAdapter<Item, LifeAdapter.ViewHolder>(DiffCallback()) {

    private class DiffCallback : DiffUtil.ItemCallback<Item>() {
        override fun areItemsTheSame(oldItem: Item, newItem: Item) = oldItem.key == newItem.key
        override fun areContentsTheSame(oldItem: Item, newItem: Item) = oldItem.num == newItem.num
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_item, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, pos: Int) {
        val position = holder.layoutPosition
        holder.bind(getItem(position))
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(item: Item) {
            itemView.findViewById<TextView>(R.id.txt_num).text = item.num.toString()
            itemView.findViewById<TextView>(R.id.txt_key).text = item.key.toString()
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

活动类:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dao = getDao(this)
        val data = dao.getAllItems()

        val adapter = LifeAdapter()
        rv.layoutManager = LinearLayoutManager(this)
        rv.adapter = adapter

        val nameObserver = Observer<List<Item>> { adapter.submitList(it) }

        data.observe(this, nameObserver)

        btn_add.setOnClickListener {
            val item = Item(Random.nextInt(0, 1000))
            runAsync { dao.insertItem(item) }
        }

        btn_change.setOnClickListener { v ->
            data.value.let {
                if (it!!.isNotEmpty()) {
                    it[0].num = 111
                    runAsync { dao.updateItem(it[0]) }
                }
            }
        }

        btn_delete.setOnClickListener { v ->
            data.value.let {
                if (it!!.isNotEmpty()) {
                    runAsync { dao.deleteItem(it[0]) }
                }
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

完整项目 - https://yadi.sk/d/7tpzDhUA-udoIQ

视频 - https://youtu.be/PZYeAfGzXBg

问题出在方法 areContentsTheSame() 中的 LifeAdapter.DiffCallback 类中。如果项目内容 (num) 发生变化,则在此方法中 newItem 和 oldItem 相同且等于新项目:

在此处输入图片说明

这意味着方法 areContentsTheSame() 总是返回 true。我通过链接(newItem === oldItem)检查了相等性,它应该是错误的。我不明白出了什么问题。通过adapter.submitList() 方法添加新列表时,newItem 和oldItem 必须不同。

Yva*_*nço 12

由于LiveData 返回相同, List您必须创建一个新的。

这里是一个更短的答案使用原来的答案toList()

recycler.observe(this, Observer{
    adapter.submitList(it.toList()) 
})
Run Code Online (Sandbox Code Playgroud)

如果您更愿意使用 kotlin 扩展,您可以执行以下操作:

fun <T> MutableLiveData<List<T>>.add(item: T) {
    val updatedItems = this.value?.toMutableList()
    updatedItems?.add(item)
    this.value = updatedItems
}
Run Code Online (Sandbox Code Playgroud)

这样你就不必添加toList(),只需使用扩展名。

  • 实际上,我正在手动更新 LiveData 对象,然后调用了 SubmitList。当我更新 LiveData 对象时,UI 首先更新。因此,几秒钟后,当调用 SubmitList 时,列表已经相同了。 (3认同)

Zak*_*nov 7

Kotlin 与数据类

adapter.submutList(list.map { it.copy() })
Run Code Online (Sandbox Code Playgroud)