我的应用程序具有RecyclerView,该视图支持拖动项目以更改其顺序。 我的应用在添加页面库之前使用ViewModel,Lifecycle,Room。处理拖动的代码很容易。
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不支持set。
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
?
答案 0 :(得分:0)
当您将PagedList和Room一起使用时,通常将其绑起来,以便通过LiveData或Rx自动反映对基础数据的更新,而这种在后台发生的更新始终会拖累您的拖放操作。所以恕我直言,您不能在所有情况下都做到100%防弹。话虽如此,您可以创建(将要执行的操作)垫片(我几乎说过“一起砍死”)。这涉及几个部分:
您需要延迟通过Room进行的实际商品更新,直到该商品被放下为止,这时您还需要清除“正在进行交换”状态。遵循以下原则:
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
}
这样,只要列表没有后台更新,并且您停留在已加载的项目列表中,您将获得流畅的拖动体验。如果您需要能够拖动“笔芯”,它将变得更加复杂。
答案 1 :(得分:0)
您需要在PagedList中移动项目。
如果要上下拖动项目以移动它们,Recyclerview的适配器需要完美地完成两件事。首先是交换数据列表中的两项,其次是通知单元重新渲染。
重新渲染很容易,移动时可以使用notifyItemMoved
更新布局,但是PagedList是不可变的,不能修改它。
当单元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
}
}
}
//Fragment
mViewModel.data.observe(this.viewLifecycleOwner, Observer {
adapter.submitList(it)
//delay for fix PagedStorageDiffHelper.computeDiff running in background thread
if (NoteListAdapter.disableAnimation) {
mViewModel.viewModelScope.launch {
delay(500)
adapter.notifyDataSetChanged() //update viewholder's binding data
NoteListAdapter.disableAnimation = false
}
}
})
//PagedListAdapter
companion object {
//heck for drag and drop to move items in PagedList
var disableAnimation = false
private val DiffCallback = object : DiffUtil.ItemCallback<Note>() {
override fun areItemsTheSame(old: Note, aNew: Note): Boolean {
return disableAnimation || old.id == aNew.id
}
override fun areContentsTheSame(old: Note, aNew: Note): Boolean {
return disableAnimation || old == aNew
}
}
}