I am implementing a RecyclerView with drag and drop support. When an item is dropped, the index column of that item will be updated in the Room database to store the updated sorting.
The problem I am facing is, when I call the Room database update after dropping the item, because the list of items is a LiveData in the ViewModel and bound to the RecyclerView through Databinding, DiffUtil will recalculate item positions and contents immediately after, which 1. adds new unwanted animations and 2. sometimes the content is not refreshed properly.
ItemTouchHelper:
val helper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN or
ItemTouchHelper.START or ItemTouchHelper.END, 0
) {
var dragFrom = -1
var dragTo = -1
override fun onMove(
recyclerView: RecyclerView, selected: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = selected.adapterPosition
val to = target.adapterPosition
if (dragFrom == -1) {
dragFrom = from
}
dragTo = to
recyclerView.adapter?.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) {
if (dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
val fromId = myAdapter.getItemId(dragFrom)
detailViewModel.updateItemIndex(fromId, calcNewIndex(dragFrom, dragTo))
}
super.clearView(recyclerView, viewHolder)
dragFrom = -1
dragTo = -1
}
})
helper.attachToRecyclerView(binding.detailRecyclerview)
DiffUtil in MyAdapter:
class NoteDiffCallback : DiffUtil.ItemCallback<MyNote>() {
override fun areItemsTheSame(oldItem: MyNote, newItem: MyNote): Boolean {
return oldItem.noteId == newItem.noteId
//return true (replacing this will mostly fix the ItemTouchHelper issues, but also removes other animations that I want, such as inserting)
}
override fun areContentsTheSame(oldItem: MyNote, newItem: MyNote): Boolean {
return oldItem == newItem
}
}
I want to change it so that DiffUtil does not interfere with ItemTouchHelper, but I still want to keep DiffUtil for the nice animation when a new note is inserted. Would appreciate suggestions.
The trick is to update the ListAdapter
items in your ItemTouchHelper.Callback
, so that the update from your Room database does nothing because the items are already equal.
class ItemTouchHelperCallback(val adapter: MyNoteAdapter) :
ItemTouchHelper.Callback() { // you need a reference to your adapter
...
override fun onMove(
recyclerView: RecyclerView, selected: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = selected.adapterPosition
val to = target.adapterPosition
if (dragFrom == -1) {
dragFrom = from
}
dragTo = to
val items = adapter.currentList.toMutableList()
Collections.swap(items, from, to)
adapter.submitList(items) // calls adapter.notifyItemMoved(), so we don't have to
return true
}
...
}