具有DiffUtil.ItemCallback的ListAdapter始终将对象视为相同 [英] ListAdapter with DiffUtil.ItemCallback always considers objects the same

查看:614
本文介绍了具有DiffUtil.ItemCallback的ListAdapter始终将对象视为相同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

物品类别:

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

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

}

适配器类

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()
        }
    }

}

活动分类:

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]) }
                }
            }
        }

    }
}

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

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

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

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

解决方案

由于LiveData 返回的结果相同 List,因此您必须创建一个新的.

这是使用toList()的原始答案的简短答案.

recycler.observe(this, Observer{
    adapter.submitList(it.toList()) 
})

如果您更喜欢使用kotlin扩展名,则可以执行以下操作:

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

这样,您不必添加toList(),而只需使用扩展名.

I`m trying to use ListAdapter with Room and LifeData. But i faced strange behavior of DiffUtil.ItemCallback - objects are always the same in areContentsTheSame() method. No problem with adding and removing object, but problem with changing the content.

Item class:

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

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

}

Adapter class

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()
        }
    }

}

Activity class:

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]) }
                }
            }
        }

    }
}

Full project - https://yadi.sk/d/7tpzDhUA-udoIQ

Video - https://youtu.be/PZYeAfGzXBg

The problem is in LifeAdapter.DiffCallback class in method areContentsTheSame(). If item content (num) changing, in this method both newItem and oldItem are the same and equals to new item:

That meant the method areContentsTheSame() always return true. I checked equality by link (newItem === oldItem) and it always false as it should be. I can`t understand what is wrong. newItem and oldItem must be different when new List added via adapter.submitList() method.

解决方案

Since LiveData returns the same List you have to create a new one.

Here is a shorter answer to the original answer by using toList().

recycler.observe(this, Observer{
    adapter.submitList(it.toList()) 
})

If you rather use a kotlin extension you can do something like this:

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

That way you don't have to add the toList() and just use the extension.

这篇关于具有DiffUtil.ItemCallback的ListAdapter始终将对象视为相同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆