Проблема при удалении элемента из списка при использовании RecycleView и DiffUtil
Обнаружил странное поведение DiffUtil при операции удаления из списка. Когда я удаляю элемент в первый раз, то всё проходит нормально. Но с последующим подобным действием удаляется не тот элемент списка, на который я нажимаю, а совершенно другой. Если же после первого удаления я пытаюсь удалить последний элемент, то приложение вылетает с IndexOutOfBoundsException. Как будто аргумент position в методе адаптера onBindViewHolder не обновляется. Подскажите, в чем может быть проблема. Код ниже.
DiffUtilCallback.kt
class DiffUtilCallback(
private val oldList: List<MyItem>,
private val newList: List<MyItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
MyAdapter.kt
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
var myList = listOf<MyItem>()
set(value) {
val diffUtil = DiffUtilCallback(field, value)
val diffResult = DiffUtil.calculateDiff(diffUtil)
field = value
diffResult.dispatchUpdatesTo(this)
}
var onClickListener: ((MyItem) -> Unit)? = null
var onLongClickListener: ((MyItem) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return myList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myItem = myList[position]
holder.bind(myItem)
holder.itemView.setOnClickListener {
onClickListener?.invoke(myItem)
}
holder.itemView.setOnLongClickListener {
onLongClickListener?.invoke(myList[position])
true
}
}
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val myTextView = itemView.findViewById<TextView>(R.id.my_text_view)
fun bind(myItem: MyItem) {
myTextView.text = myItem.text
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val myList = mutableListOf(
MyItem(0, "1"),
MyItem(1, "2"),
MyItem(2, "3"),
MyItem(3, "4"),
MyItem(4,"5"),
)
private var adapter: MyAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
adapter = MyAdapter()
recyclerView.adapter = adapter
adapter?.myList = myList.toList()
adapter?.onLongClickListener = {
deleteItem(it)
}
adapter?.onClickListener = { myItem ->
Log.d("ITEM", myItem.toString())
}
}
private fun deleteItem(myItem: MyItem) {
myList.remove(myItem)
adapter?.myList = myList.toList()
}
}
MyItem.kt
data class MyItem(
val id: Int,
val text: String
)
Ответы (1 шт):
Попробуйте перенести код обработчика нажатия в метод bind(). Полагаю, что это как-то связано со старыми обработчиками viewHolder.
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val myTextView = itemView.findViewById<TextView>(R.id.my_text_view)
fun bind(myItem: MyItem) {
myTextView.text = myItem.text
itemView.setOnClickListener {
onClickListener?.invoke(myItem)
}
itemView.setOnLongClickListener {
onLongClickListener?.invoke(myItem)
true
}
}
}