在这里我发布了对我有用的解决方案。它基于@prodaea 的答案,该答案具有正确的方法但功能不全。它缺少 firebase 数据库更新和对 ChangeEventType.CHANGED 的额外检查,以避免 firebase 更新时动画中断。
代码存在于适配器中,该适配器扩展了FirebaseRcyclerAdapter 并使用单个方法onItemMove(fromPosition: Int, toPosition: Int): Boolean 实现接口:
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
snapshots[fromPosition].sort = toPosition.toDouble()
snapshots[toPosition].sort = fromPosition.toDouble()
// update recycler locally to complete the animation
notifyItemMoved(fromPosition, toPosition)
updateFirebase(fromPosition, toPosition)
return true
}
private fun updateFirebase(fromPos: Int, toPos: Int) {
// flag which prevents callbacks from interrupting the drag animation when firebase database updates
hasDragged = true
val firstPath = getRef(fromPos).getPath()
val secondPath = getRef(toPos).getPath()
Log.d(_tag, "onItemMove: firstPath: $firstPath, secondPath: $secondPath")
val updates = mapOf(
Pair(firstPath + "/sort", snapshots[fromPos].sort),
Pair(secondPath + "/sort", snapshots[toPos].sort))
getRef(fromPos).root.updateChildren(updates)
Log.d(_tag, "updateFirebase, catA: \"${snapshots[fromPos]}\", catB: \"${snapshots[toPos]}\"")
}
private fun DatabaseReference.getPath() = toString().substring(root.toString().length)
override fun onChildChanged(type: ChangeEventType,
snapshot: DataSnapshot,
newIndex: Int,
oldIndex: Int) {
Log.d(_tag, "onChildChanged:else, type:$type, new: $newIndex, old: $oldIndex, hasDragged: $hasDragged, snapshot: $snapshot")
when (type) {
// avoid the drag animation interruption by checking against 'hasDragged' flag
ChangeEventType.MOVED -> if (!hasDragged) {
super.onChildChanged(type, snapshot, newIndex, oldIndex)
}
ChangeEventType.CHANGED -> if (!hasDragged) {
super.onChildChanged(type, snapshot, newIndex, oldIndex)
}
else -> {
super.onChildChanged(type, snapshot, newIndex, oldIndex)
}
}
}
override fun onDataChanged() {
hasDragged = false
Log.d(_tag, "onDataChanged, hasDragged: $hasDragged")
}