I have a list of the RecyclerView. And I made a swipe removal. Then I made a Snackbar in MainActivity to undo the removal:
val onSwipe = object : OnSwipe(this) {
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
when (direction) {
ItemTouchHelper.RIGHT -> {
adapter.removeItem(
viewHolder.absoluteAdapterPosition
)
Snackbar.make(binding.rv, "Deleted", Snackbar.LENGTH_SHORT)
.apply {
setAction("Undo") {
adapter.restoreItem(
viewHolder.absoluteAdapterPosition)
}
show()
}
}
}
}
}
Code in adapter:
fun removeItem(pos: Int) {
listArray.removeAt(pos)
notifyItemRemoved(pos)
}
fun restoreItem(pos: Int) {
listArray.add(pos, listArray[pos])
notifyItemInserted(pos)
}
And when I make the undo operation, my app stops, and I see this in a Logcat:
java.lang.ArrayIndexOutOfBoundsException: length=10; index=-1
at java.util.ArrayList.get(ArrayList.java:439)
at com.example.databaselesson.recyclerView.ExpensesAdapter.restoreItem(ExpensesAdapter.kt:79)
at com.example.databaselesson.MainActivity2$onSwipe$1.onSwiped$lambda-1$lambda-0(MainActivity2.kt:391)
at com.example.databaselesson.MainActivity2$onSwipe$1.$r8$lambda$AhJR3pu-3ynwFvPp66LdaLyFdB0(Unknown Source:0)
at com.example.databaselesson.MainActivity2$onSwipe$1$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
Please, help
If you need more code, please, write, and I will send you it
There are two issues here.
1st: Call viewHolder.absoluteAdapterPosition
after notifyItemRemoved
shall return -1
This match the exception in your Logcat since it is telling you that you are trying to get index=-1 from listArray
.
val onSwipe = object : OnSwipe(this) {
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
when (direction) {
ItemTouchHelper.RIGHT -> {
adapter.removeItem(
viewHolder.absoluteAdapterPosition //<==Let's say position return 8
)
Snackbar.make(binding.rv, "Deleted", Snackbar.LENGTH_SHORT)
.apply {
setAction("Undo") {
adapter.restoreItem(
viewHolder.absoluteAdapterPosition) //<==Deselected item so it shall return -1
}
show()
}
}
}
}
}
2nd: You haven't cached the item object so it will fail to retrieve the correct data
// Assume that `listArray` = ["A", "B", "C"], `pos` = 1
fun removeItem(pos: Int) {
listArray.removeAt(pos) = ["A", "C"]
notifyItemRemoved(pos)
}
// `listArray` = ["A", "C"], `pos` = 1 (Assume you get the correct target pos)
fun restoreItem(pos: Int) {
listArray.add(pos, listArray[pos]) //`listArray[1]` = "C", listArray = ["A", "C", "C"]
notifyItemInserted(pos)
}
In order to resolve this, you will need to cache both the position and item object in onSwiped
call
val onSwipe = object : OnSwipe(this) {
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
when (direction) {
ItemTouchHelper.RIGHT -> {
val cachedPosition = viewHolder.absoluteAdapterPosition // cache position!
val cachedItem = listArray[cachedPosition] // cache item!
adapter.removeItem(cachedPosition)
Snackbar.make(binding.rv, "Deleted", Snackbar.LENGTH_SHORT)
.apply {
setAction("Undo") {
adapter.restoreItem(cachedPosition, cachedItem)
}
show()
}
}
}
}
}