当视图更新 Android Kotlin 时,DiffUtil 不与 ListAdpater 一起使用
Posted
技术标签:
【中文标题】当视图更新 Android Kotlin 时,DiffUtil 不与 ListAdpater 一起使用【英文标题】:DiffUtil Not working with ListAdpater when view is updating Android Kotlin 【发布时间】:2021-12-10 09:18:10 【问题描述】:嘿,我有 Reyclerview 和 DiffUtill 使用 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_
android:layout_
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_>
<TextView
android:id="@+id/intNumber"
android:layout_
android:layout_
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
【问题讨论】:
【参考方案1】:您需要确保每次调用 submitList
时传递不同的 List 实例。如果您传递一个 List,对其进行变异,然后再次传递同一个 List 实例,DiffUtil 只会将同一个列表与自身进行比较,因此它将假定列表中没有任何变化并且不会更新任何内容。它对您第一次提交时 List 包含的内容没有某种记忆。
为了进一步概括,您根本不能将可变列表与 ListAdapter 一起使用。 ListAdapter 假定您传递给submitList
的列表没有改变。如果你对其进行变异,可能会出现意想不到的错误。
在您的代码中解决此问题的两种方法。
每次传递列表时创建一个只读副本:
intAdapter.submitList(list.toList())
根本不要使用 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)
旁注:当您将var
与Mutable____
结合使用时,这应该是一个危险信号。您需要两种不同的方式来改变某些东西应该很少见。这很容易出错。
【讨论】:
你能看看这个新的issue【参考方案2】:您需要提交一个新的List
,目前您正在提交相同的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)
这个逻辑的例子:
【讨论】:
以上是关于当视图更新 Android Kotlin 时,DiffUtil 不与 ListAdpater 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
当用户滚动时如何显示或隐藏日期文本视图就像whatsapp聊天android Kotlin
DiffUtil 不刷新观察者调用android kotlin中的视图
在 Android 视图模型中的内部网络更改回调时 LiveData 未触发 - Kotlin
添加新文档时,kotlin firestore 未更新我的 android 应用程序