PagedListAdapter 和 ItemTouchHelper with Room - 你如何在 onMove() 期间更新
PagedListAdapter and ItemTouchHelper with Room - How do you update during onMove()
我遇到的问题是,在 RecyclerView
上拖动项目时,onMove()
回调无法更改项目数组的实际顺序,因为该数组由 PagedListAdapter
。如果我在 DB 上执行它,这会发生在后台线程上,因为 Room
要求我这样做(无论如何我真的应该在后台线程上执行 DB),正如您想象的那样,这是行不通的以及项目的拖动。
我对 ItemTouchHelper
工作原理的理解是,在 onMove()
上,我必须重新排列我想要显示的任何内容,然后我可以在 clearView()
上保存到数据库。但我无法在 onMove()
上重新排列。
这是对我当前代码的简化,它可以工作,但拖动的视觉提示效果不佳。
inner class SwipeCallback() : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val item1 = getItem(viewHolder.adapterPosition)
val item2 = getItem(target.adapterPosition)
if (item1 != null && item2 != null) {
//BASICALLY JUST SWAPPING THE TWO POSITIONS ON THE OBJECTS but not doing anything with the adapter array because the PagedListAdapter doesn't allow me to modify the array as far I know.
swapItems(item1,item2)
}
return true
}
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isLongPressDragEnabled(): Boolean {
return false
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder?.itemView?.alpha = 1.0f
//THIS IS WHERE I UPDATED THE DB, currentList is the PagedListAdapter array. This is an inner class of my PagedListAdapter.
currentList?.let {updateList(it) }
}
}
那么我怎样才能使这个工作可视化呢?
谢谢。
我假设您正在使用房间数据库将分页数据加载到 UI。事实上,如果使用分页库,那是使我们的列表能够响应数据动态变化的唯一方法。
在adapter中,只需要在onMove()函数中通知数据库发生变化
class SwipeCallback(
private val adapter: HomeSpotAdapter,
private val onItemMove: (from: Int, to: Int) -> Unit
) : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = viewHolder.adapterPosition
val to = target.adapterPosition
val item1 = adapter.getItem(from)
val item2 = adapter.getItem(to)
if (item1 != null && item2 != null) {
// Notify database to swap items here
onItemMove(from, to)
return true
}
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun isLongPressDragEnabled(): Boolean {
// It has to be true
return true
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
// Don't need to do anything here
}
}
在片段中,我们将 onItemMove lambda 函数传递给适配器
ItemTouchHelper(HomeSpotAdapter.SwipeCallback(adapter) { from, to ->
viewModel.swapItems(from, to)
}).apply {
attachToRecyclerView(recyclerView.getRecycler())
}
要交换数据库中的项目,基本上我们可以像这样使用 swapItems() 函数
fun swapItem(from: Int, to: Int) {
// I'm using executor to run query in worker thread here, but you can choose whatever you want
Executors.newSingleThreadExecutor().execute {
val item1 = db.HomeSpotDao().getSpot(from)
val item2 = db.HomeSpotDao().getSpot(to)
// Swap index of 2 items
val newItem1 = item1.copy()
newItem1.indexInResponse = item2.indexInResponse
val newItem2 = item2.copy()
newItem2.indexInResponse = item1.indexInResponse
db.HomeSpotDao().updateSpot(newItem1)
db.HomeSpotDao().updateSpot(newItem2)
}
}
您可以在 Google 示例中查看此 class 以了解如何使用 indexInResponse 来跟踪列表中项目的索引
终于找到了正确的解决方案。
在 onMove
你必须 notifyItemMoved(from,to)
然后在 clearView
你实际上使数据库移动。根据您的移动涉及的内容,您可能必须将第一个 from
存储在 notifyItemMoved
上,然后计算它被移动的位置 to
并在您的数据库上修改其间的所有时间。
我遇到的问题是,在 RecyclerView
上拖动项目时,onMove()
回调无法更改项目数组的实际顺序,因为该数组由 PagedListAdapter
。如果我在 DB 上执行它,这会发生在后台线程上,因为 Room
要求我这样做(无论如何我真的应该在后台线程上执行 DB),正如您想象的那样,这是行不通的以及项目的拖动。
我对 ItemTouchHelper
工作原理的理解是,在 onMove()
上,我必须重新排列我想要显示的任何内容,然后我可以在 clearView()
上保存到数据库。但我无法在 onMove()
上重新排列。
这是对我当前代码的简化,它可以工作,但拖动的视觉提示效果不佳。
inner class SwipeCallback() : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val item1 = getItem(viewHolder.adapterPosition)
val item2 = getItem(target.adapterPosition)
if (item1 != null && item2 != null) {
//BASICALLY JUST SWAPPING THE TWO POSITIONS ON THE OBJECTS but not doing anything with the adapter array because the PagedListAdapter doesn't allow me to modify the array as far I know.
swapItems(item1,item2)
}
return true
}
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun isLongPressDragEnabled(): Boolean {
return false
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder?.itemView?.alpha = 1.0f
//THIS IS WHERE I UPDATED THE DB, currentList is the PagedListAdapter array. This is an inner class of my PagedListAdapter.
currentList?.let {updateList(it) }
}
}
那么我怎样才能使这个工作可视化呢?
谢谢。
我假设您正在使用房间数据库将分页数据加载到 UI。事实上,如果使用分页库,那是使我们的列表能够响应数据动态变化的唯一方法。
在adapter中,只需要在onMove()函数中通知数据库发生变化
class SwipeCallback(
private val adapter: HomeSpotAdapter,
private val onItemMove: (from: Int, to: Int) -> Unit
) : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val from = viewHolder.adapterPosition
val to = target.adapterPosition
val item1 = adapter.getItem(from)
val item2 = adapter.getItem(to)
if (item1 != null && item2 != null) {
// Notify database to swap items here
onItemMove(from, to)
return true
}
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun isLongPressDragEnabled(): Boolean {
// It has to be true
return true
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
// Don't need to do anything here
}
}
在片段中,我们将 onItemMove lambda 函数传递给适配器
ItemTouchHelper(HomeSpotAdapter.SwipeCallback(adapter) { from, to ->
viewModel.swapItems(from, to)
}).apply {
attachToRecyclerView(recyclerView.getRecycler())
}
要交换数据库中的项目,基本上我们可以像这样使用 swapItems() 函数
fun swapItem(from: Int, to: Int) {
// I'm using executor to run query in worker thread here, but you can choose whatever you want
Executors.newSingleThreadExecutor().execute {
val item1 = db.HomeSpotDao().getSpot(from)
val item2 = db.HomeSpotDao().getSpot(to)
// Swap index of 2 items
val newItem1 = item1.copy()
newItem1.indexInResponse = item2.indexInResponse
val newItem2 = item2.copy()
newItem2.indexInResponse = item1.indexInResponse
db.HomeSpotDao().updateSpot(newItem1)
db.HomeSpotDao().updateSpot(newItem2)
}
}
您可以在 Google 示例中查看此 class 以了解如何使用 indexInResponse 来跟踪列表中项目的索引
终于找到了正确的解决方案。
在 onMove
你必须 notifyItemMoved(from,to)
然后在 clearView
你实际上使数据库移动。根据您的移动涉及的内容,您可能必须将第一个 from
存储在 notifyItemMoved
上,然后计算它被移动的位置 to
并在您的数据库上修改其间的所有时间。