目前,我有一个实现新ListAdapter的RecyclerView,它使用submitList来区分元素,并继续自动更新UI。
最近我不得不使用著名的ItemTouchHelper来实现对列表的拖放。下面是我的实现,非常简单:
class DraggableItemTouchHelper(private val adapter: DestinationsAdapter) : ItemTouchHelper.Callback() {
private val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
private val swipeFlags = 0
override fun isLongPressDragEnabled() = false
override fun isItemViewSwipeEnabled() = false
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val oldPos = viewHolder.adapterPosition
val newPos = target.adapterPosition
adapter.swap(oldPos, newPos)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
}
这是我在适配器中的交换函数:
fun swap(from: Int, to: Int) {
submitList(ArrayList(currentList).also {
it[from] = currentList[to]
it[to] = currentList[from]
})
}
除了移动列表中的第一个项目外,其他一切都很正常。有时它的表现还可以,但大多数情况下(比如90%),即使将它移动到第二个项目的上方(例如将第一个项目移动到第二个位置),它也会捕捉几个位置。新的位置似乎是随机的,我无法找出问题所在。
作为一个指导,我使用了https://github.com/material-components/material-components-android的例子来实现拖放和他们的(简单)列表和布局工作得很好。我的列表是有点复杂,因为它是在一个视图页,使用导航组件和有许多其他视图约束在一起,在该屏幕上,但我不认为这应该是相关的。
在这一点上,我甚至不知道如何在网络上搜索这个问题了。我找到的最接近的解决方案可能是https://issuetracker.google.com/issues/37018279,但在实现并具有相同的行为后,我认为这是因为我使用了不同的ListAdapter,并异步更新列表,而解决方案使用了RecyclerView.Adapter,它使用notifyItemMoved和其他类似的方法。
正在切换到RecyclerView。适配器不是解决方案。
2条答案
按热度按时间8yoxcaq71#
这似乎是
AsyncListDiffer
中的一个bug,它被ListAdapter
暗中使用。我的解决方案允许你在需要的时候手动比较更改。然而,它相当笨拙,使用反射,可能不能在未来的appcompat
版本中工作(我测试的版本是1.3.0
)。由于
mDiffer
在ListAdapter
中是私有的,并且您需要直接使用它,因此您必须创建自己的ListAdapter
实现(您可以只复制原始源代码)。然后添加以下方法:这个方法会以无消息方式变更基础
AsyncListDiffer
中的目前清单,而不会触发任何差异,如同submitList()
一样。生成的文件应如下所示:
现在,您需要更改适配器实现,以从自定义
ListAdapter
而不是androidx.recyclerview.widget.ListAdapter
继承。最后,您需要更改适配器的
swap()
方法实现,以使用setListWithoutDiffing()
和notifyItemMoved()
方法:另一种解决方案是创建一个自定义的
AsyncListDiffer
版本,让您在没有反射的情况下做同样的事情,但这种方式似乎更容易。我还将提交一个支持开箱即用的手动差异化的功能请求,并使用Google问题跟踪器链接更新问题。omtl5h9j2#
我在我的适配器中保留了一个项目的副本,修改了副本,并使用
notifyItemMoved
在用户拖动时更新UI。我只在用户完成拖动后保存更新的项目/顺序。这对我很有效,因为1)我有一个固定长度的9个项目的列表; 2)我能够使用clearView
来知道拖动何时结束。列表适配器-Kotlin:
项目触摸助手.回调()-Kotlin:
我的项目视图持有者-Kotlin
我在MyItem上也有一个
itemOrder
字段。当我将它保存到DB时,我使用重新排序的条目的索引更新了该字段。当我交换条目时,我可能会更新每个条目的itemOrder
字段,但这似乎毫无意义(我只是在拖动完成后计算新的顺序)。我正在使用数据库中的LiveData。我发现在最后一次数据库保存后视图“闪烁”,因为我更改了所有项目上的
itemOrder
,并在适配器列表中移动了项目。如果您遇到这种情况,并且不喜欢它,只是暂时禁用回收程序视图项目动画(我通过在拖动后将其设置为null,并在RecyclerView/Adapter中更新列表后恢复它来实现这一点)。这对我和我的具体情况都有效。如果你需要更多细节,请告诉我。