我有一个 RecyclerView 列表。我做了一个滑动删除。然后我在 MainActivity 中做了一个 Snackbar 来撤销删除:
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()
}
}
}
}
}
适配器中的代码:
fun removeItem(pos: Int) {
listArray.removeAt(pos)
notifyItemRemoved(pos)
}
fun restoreItem(pos: Int) {
listArray.add(pos, listArray[pos])
notifyItemInserted(pos)
}
当我进行撤消操作时,我的应用程序停止了,我在 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)
求助
如果您需要更多代码,请写信,我会发送给您
当您删除项目并执行
notifyItemRemoved
时,用于显示该项目的ViewHolder
将从列表中删除。因为它没有显示任何东西,它的 absoluteAdapterPosition
被设置为 NO_POSITION
,或者 -1:
退货
int
从
的角度来看项目的适配器位置,如果它仍然存在于适配器中并绑定到有效项目。RecyclerView
如果项目已从适配器中移除,NO_POSITION
已在最后一次布局传递后调用或 ViewHolder 已被回收。notifyDataSetChanged
所以当您点击撤消按钮时,
viewholder
将返回 -1,这不是您的数据列表的有效索引!
您可能应该存储要删除的实际位置:
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
// get the position first, and store that value
val position = viewHolder.absoluteAdapterPosition
when (direction) {
ItemTouchHelper.RIGHT -> {
// using the position we stored
adapter.removeItem(position)
// you don't have to use apply here if you don't want - it's designed
// to be chained (fluent interface where each call returns the Snackbar)
Snackbar.make(binding.rv, "Deleted", Snackbar.LENGTH_SHORT)
// using that fixed position value again
.setAction("Undo") { adapter.restoreItem(position) }
.show()
}
}
}
这样你就可以删除一个特定的项目位置,如果点击了撤消按钮,你可以使用 相同的位置值 来恢复它。你不依赖于正在使用的
ViewHolder
的状态。
还有这个:
fun restoreItem(pos: Int) {
listArray.add(pos, listArray[pos])
notifyItemInserted(pos)
}
好像没有恢复什么?它只是在同一位置插入项目
pos
的副本。由于您的 removeItem
实际上从列表中删除了该项目,因此除非您将其存储在某个地方,否则无法取回它。你可以有一个 lastDeletedItem
变量,你在 removeItem
中更新它 restoreItem
恢复:
var lastDeletedItem: Item? = null
fun removeItem(pos: Int) {
// store the deleted item
lastDeletedItem = listArray[pos]
listArray.removeAt(pos)
notifyItemRemoved(pos)
}
fun restoreItem(pos: Int) {
// restore the last thing that was deleted at this position
lastDeletedItem?.let {
listArray.add(pos, it)
notifyItemInserted(pos)
}
}
但是你有一个地方被删除的项目,另一个位置(小吃店 lambda)所以你可能想把它们放在一起 - 将
lastDeletedPosition
存储在 removeItem
中并在 restoreItem
中引用它
(不要传递 pos
),或者让 restoreItem
在存储当前适配器位置时获取 pos
和 item
并在滑动回调中获取项目
这里有两个问题
1st:在
viewHolder.absoluteAdapterPosition
之后调用notifyItemRemoved
应返回-1
这与您的 Logcat 中的异常匹配,因为它告诉您您正在尝试从
listArray
. 获取索引=-1
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()
}
}
}
}
}
第二:您没有缓存项目对象,因此它将无法检索正确的数据
// 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)
}
为了解决这个问题,您需要在
onSwiped
调用中缓存位置和项目对象
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()
}
}
}
}
}
如果您使用的是 FlexLayoutManager,只需添加此代码
FlexboxLayoutManager flexboxLayoutManager = new FlexboxLayoutManager(mContext);
flexboxLayoutManager.setFlexDirection(FlexDirection.ROW);
flexboxLayoutManager.setJustifyContent(JustifyContent.FLEX_START);
并在 XML 中为 Recyclerview 添加固定高度