当视图更新 Android Kotlin 时,DiffUtil 不能与 ListAdpater 一起使用 [英] DiffUtil Not working with ListAdpater when view is updating Android Kotlin

查看:61
本文介绍了当视图更新 Android Kotlin 时,DiffUtil 不能与 ListAdpater 一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Hey I have Reyclerview with DiffUtill using ListAdapter. I added element through submitList function. But when updating the list view is not redrawing the element. Until I used notifyDataSetChanged() or setting adapter again. So what the use case of DiffUtill?. What is the proper way of doing to redraw item when item is updated in list as well as in reyclerview.

主活动

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

内部适配器

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>

推荐答案

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

You need to ensure you pass a different instance of List each time you call submitList. If you pass a List, mutate it, and then pass that same List instance again, DiffUtil is only comparing the same list to itself, so it will assume nothing has changed in the list and won't update anything. It doesn't have some sort of memory of what the List contained back when you first submitted it.

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

To generalize further, you must not use a mutable List with ListAdapter at all. ListAdapter assumes the list you pass to submitList does not change. If you mutate it, there can be unexpected bugs.

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

Two ways to resolve this in your code.

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

  1. Create a read-only copy of the list each time you pass it:

intAdapter.submitList(list.toList())

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

Don't use a MutableList at all. Create a new List every time you modify what should be in the List. This is the simpler, less error-prone solution.

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____ 结合使用时,这应该是一个危险信号.您需要两种不同的方式来改变某些东西的情况应该很少见.这很容易出错.

Side note: when you have var combined with Mutable____ that should be kind of a red flag. It should be rare that you need two different ways to change something. That is error-prone.

这篇关于当视图更新 Android Kotlin 时,DiffUtil 不能与 ListAdpater 一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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