更新视图时 DiffUtil 不使用 ListAdpater Android Kotlin

DiffUtil Not working with ListAdpater when view is updating Android Kotlin

嘿,我有 ReyclerviewDiffUtill 使用 ListAdapter。我通过 submitList 函数添加了元素。但是更新列表视图时并没有重绘元素。直到我再次使用 notifyDataSetChanged() 或设置 adapter 。那么 DiffUtill 的用例是什么?当项目在列表和 reyclerview 中更新时,重绘项目的正确方法是什么。

MainActivity

class MainActivity : AppCompatActivity() {

    private var list = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    private lateinit var binding: ActivityMainBinding
    var i = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        Log.e("List", " $list")
        val intAdapter = IntAdapter()
        binding.recylerview.adapter = intAdapter
        intAdapter.submitList(list)
        binding.button.setOnClickListener {
            list.add(++i)
            intAdapter.submitList(list)
//            binding.recylerview.adapter = intAdapter
//            intAdapter.notifyDataSetChanged()
        }
    }
}

IntAdapter

class IntAdapter : ListAdapter<Int, IntViewHolder>(comparator) {

    companion object {
        private val comparator = object : DiffUtil.ItemCallback<Int>() {
            override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
                return oldItem == newItem
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntViewHolder {
        return IntViewHolder(
            IntLayoutBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: IntViewHolder, position: Int) {
        holder.bindItem(getItem(position))
    }

}

IntViewHolder

class IntViewHolder(val binding: IntLayoutBinding) : RecyclerView.ViewHolder(binding.root) {

    fun bindItem(item: Int?) {
        binding.intNumber.text = item.toString()
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/recylerview"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="add"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

int_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/intNumber"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

您需要提交一个新的 List,目前您提交的是相同的 List,它已经被修改了。

在你的点击监听器中尝试类似的东西:

binding.button.setOnClickListener {
    val current = intAdapter.currentList
    val update = current + (current.size + 1) // returns a new list with added value
    intAdapter.submitList(update)
}

具有此逻辑的示例:

您需要确保每次调用 submitList 时都传递不同的 List 实例。如果您传递一个 List,改变它,然后再次传递同一个 List 实例,DiffUtil 只会将同一个列表与自身进行比较,因此它会假定列表中没有任何更改并且不会更新任何内容。当您第一次提交时,它对列表中包含的内容没有某种记忆。

为了进一步概括,您根本不能将可变列表与 ListAdapter 一起使用。 ListAdapter 假定您传递给 submitList 的列表不会改变。如果你改变它,可能会出现意想不到的错误。

在您的代码中解决此问题的两种方法。

  1. 每次传递时创建列表的只读副本:

    intAdapter.submitList(list.toList())

  2. 根本不要使用 MutableList。每次修改列表中应包含的内容时都创建一个新列表。这是更简单、不易出错的解决方案。

class MainActivity : AppCompatActivity() {

    private var list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    private lateinit var binding: ActivityMainBinding
    var i = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        Log.e("List", " $list")
        val intAdapter = IntAdapter()
        binding.recylerview.adapter = intAdapter
        intAdapter.submitList(list)
        binding.button.setOnClickListener {
            list += ++i // create a new list from old list contents plus a new item
            intAdapter.submitList(list)
        }
    }
}

旁注:当您将 varMutable____ 结合使用时,这应该是一个危险信号。很少需要两种不同的方式来改变某事。那是容易出错的。