更新视图时 DiffUtil 不使用 ListAdpater Android Kotlin
DiffUtil Not working with ListAdpater when view is updating Android Kotlin
嘿,我有 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_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
的列表不会改变。如果你改变它,可能会出现意想不到的错误。
在您的代码中解决此问题的两种方法。
每次传递时创建列表的只读副本:
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____
结合使用时,这应该是一个危险信号。很少需要两种不同的方式来改变某事。那是容易出错的。
嘿,我有 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_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
的列表不会改变。如果你改变它,可能会出现意想不到的错误。
在您的代码中解决此问题的两种方法。
每次传递时创建列表的只读副本:
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____
结合使用时,这应该是一个危险信号。很少需要两种不同的方式来改变某事。那是容易出错的。