Проблема при удалении элемента из списка при использовании 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 шт):

Автор решения: Boronov

Попробуйте перенести код обработчика нажатия в метод 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
        }
    }
}
→ Ссылка