Android RecyclerView中的拖动项如何与分页库一起工作?
我的应用程序有一个RecyclerView,它支持拖动项目以更改其顺序。 我的应用程序在添加分页库之前使用ViewModel、Lifecycle和Room。处理拖动的代码也很简单Android RecyclerView中的拖动项如何与分页库一起工作?,android,android-recyclerview,android-paging,Android,Android Recyclerview,Android Paging,我的应用程序有一个RecyclerView,它支持拖动项目以更改其顺序。 我的应用程序在添加分页库之前使用ViewModel、Lifecycle和Room。处理拖动的代码也很简单 override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { val oPosition = viewHol
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val oPosition = viewHolder.adapterPosition
val tPosition = target.adapterPosition
Collections.swap(adapter?.data ,oPosition,tPosition)
adapter?.notifyItemMoved(oPosition,tPosition)
//save to db
return true
}
但是,在我使用分页库之后
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val oPosition = viewHolder.adapterPosition
val tPosition = target.adapterPosition
Collections.swap(adapter.currentList,oPosition,tPosition)
adapter.notifyItemMoved(oPosition,tPosition)
return true
}
我的应用程序崩溃,因为PagedListAdapter.currentList不支持设置
java.lang.UnsupportedOperationException
at java.util.AbstractList.set(AbstractList.java:132)
at java.util.Collections.swap(Collections.java:539)
at gmail.zebulon988.tasklist.ui.TaskListFragment$MyItemTouchCallback.onMove(TaskListFragment.kt:119).
然后我更改代码
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val oPosition = viewHolder.adapterPosition
val tPosition = target.adapterPosition
Log.d("TAG","onMove:o=$oPosition,t=$tPosition")
val oTask = (viewHolder as VH).task
val tTask = (target as VH).task
if(oTask != null && tTask != null){
val tmp = oTask.order
oTask.order = tTask.order
tTask.order = tmp
tasklistViewModel.insertTask(oTask,tTask)
}
return true
}
此代码直接更改任务在db中的顺序,库通过db更改更新显示顺序。然而,动画是丑陋的
有没有一种方法可以优雅地将
onMove
和paging library
一起使用?当您使用带有文件室的页面列表时,您通常会将其捆绑起来,以便通过LiveData或Rx自动反映对底层数据的更新,而这种在后台发生的更新总是会打乱您的拖放操作。所以我认为你不可能在所有情况下都做到100%防弹。话虽如此,您可以创建一个垫片(我几乎说是“hack-together”),它可以实现您想要的功能。这涉及几个方面:
fun swapItems(fromPosition: Int, toPosition: Int) {
swapInfo = SwapInfo(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
}
override fun getItem(position: Int): T? {
return swapInfo?.let {
when (position) {
it.fromPosition -> super.getItem(it.toPosition)
it.toPosition -> super.getItem(it.fromPosition)
else -> super.getItem(position)
}
} ?: super.getItem(position)
}
fun clearSwapInfo() {
swapInfo = null
}
通过这种方式,只要列表没有背景更新,并且保持在已加载的项目列表中,您就可以获得平滑的拖动体验。如果您需要能够通过“重新填充”进行拖动,则会变得更加复杂。当您使用带有文件室的页面列表时,您通常会将其捆绑起来,以便通过LiveData或Rx自动反映对基础数据的更新,而在后台发生的此类更新总是会打乱您的拖放操作。所以我认为你不可能在所有情况下都做到100%防弹。话虽如此,您可以创建一个垫片(我几乎说是“hack-together”),它可以实现您想要的功能。这涉及几个方面:
fun swapItems(fromPosition: Int, toPosition: Int) {
swapInfo = SwapInfo(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
}
override fun getItem(position: Int): T? {
return swapInfo?.let {
when (position) {
it.fromPosition -> super.getItem(it.toPosition)
it.toPosition -> super.getItem(it.fromPosition)
else -> super.getItem(position)
}
} ?: super.getItem(position)
}
fun clearSwapInfo() {
swapInfo = null
}
通过这种方式,只要列表没有背景更新,并且保持在已加载的项目列表中,您就可以获得平滑的拖动体验。如果您需要能够拖动“重新填充”,则会变得更加复杂。您需要检查页面列表中的移动项目 如果你想上下拖动物品来移动它们,Recyclerview的适配器需要完美地完成两件事。第一个是交换datalist中的两个项,第二个是通知单元格重新呈现 重新渲染很容易,移动时可以使用
notifyItemMoved
更新布局,但PagedList是不可变的,您无法修改它
当cell ui已经更改但数据源没有更改时,会出现动画错误。您不能覆盖recyclerview内部的渲染逻辑,但可以检查PagedStorageDiffHelper.computeDiff的结果以修复动画错误
最后,不要忘记在拖放之后检索最新的数据
//ItemTouchHelperAdapter
override fun onItemStartMove() {
//the most the most updated data; mimic pagedlist, but can be modified;
tempList = adapter.currentList?.toMutableList()
toUpdate = mutableListOf()
}
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
val itemFrom = tempList?.get(fromPosition) ?: return false
val itemTo = tempList?.get(toPosition) ?: return false
//change order property for data itself
val order = itemTo.order
itemTo.order = itemFrom.order
itemFrom.order = order
//save them for later update db in batch
toUpdate?.removeAll { it.id == itemFrom.id || it.id == itemTo.id }
toUpdate?.add(itemFrom)
toUpdate?.add(itemTo)
//mimic mutable pagedlist, for get next time get correct items for continuing drag
Collections.swap(tempList!!, fromPosition, toPosition)
//update ui
adapter.notifyItemMoved(fromPosition, toPosition)
return true
}
override fun onItemEndMove() {
tempList = null
if (!toUpdate.isNullOrEmpty()) {
mViewModel.viewModelScope.launch(Dispatchers.IO) {
//heck, fix animation bug because pagedList did not really change.
NoteListAdapter.disableAnimation = true
mViewModel.updateInDB(toUpdate!!)
toUpdate = null
}
}
}
//PagedListAdapter
伴星{
//选中“拖放”以移动页面列表中的项目
var disableAnimation=false
private val DiffCallback=对象:DiffUtil.ItemCallback(){
覆盖乐趣项相同(旧:注释,新:注释):布尔值{
返回disableAnimation | | old.id==aNew.id
}
覆盖内容相同(旧:注释,新:注释):布尔值{
返回disableAnimation | | old==重新
}
}
}
您需要检查页面列表中的移动项目
如果你想上下拖动物品来移动它们,Recyclerview的适配器需要完美地完成两件事。第一个是交换datalist中的两个项,第二个是通知单元格重新呈现
重新渲染很容易,移动时可以使用notifyItemMoved
更新布局,但PagedList是不可变的,您无法修改它
当cell ui已经更改但数据源没有更改时,会出现动画错误。您不能覆盖recyclerview内部的渲染逻辑,但可以检查PagedStorageDiffHelper.computeDiff的结果以修复动画错误
最后,不要忘记在拖放之后检索最新的数据
//ItemTouchHelperAdapter
override fun onItemStartMove() {
//the most the most updated data; mimic pagedlist, but can be modified;
tempList = adapter.currentList?.toMutableList()
toUpdate = mutableListOf()
}
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
val itemFrom = tempList?.get(fromPosition) ?: return false
val itemTo = tempList?.get(toPosition) ?: return false
//change order property for data itself
val order = itemTo.order
itemTo.order = itemFrom.order
itemFrom.order = order
//save them for later update db in batch
toUpdate?.removeAll { it.id == itemFrom.id || it.id == itemTo.id }
toUpdate?.add(itemFrom)
toUpdate?.add(itemTo)
//mimic mutable pagedlist, for get next time get correct items for continuing drag
Collections.swap(tempList!!, fromPosition, toPosition)
//update ui
adapter.notifyItemMoved(fromPosition, toPosition)
return true
}
override fun onItemEndMove() {
tempList = null
if (!toUpdate.isNullOrEmpty()) {
mViewModel.viewModelScope.launch(Dispatchers.IO) {
//heck, fix animation bug because pagedList did not really change.
NoteListAdapter.disableAnimation = true
mViewModel.updateInDB(toUpdate!!)
toUpdate = null
}
}
}
//PagedListAdapter
伴星{
//选中“拖放”以移动页面列表中的项目
var disableAnimation=false
private val DiffCallback=对象:DiffUtil.ItemCallback(){
覆盖乐趣项相同(旧:注释,新:注释):布尔值{
返回disableAnimation | | old.id==aNew.id
}
覆盖内容相同(旧:注释,新:注释):布尔值{
返回disableAnimation | | old==重新
}
}
}