【问题标题】:how to use drag and drop on recycler view using firebase realtime database如何使用 Firebase 实时数据库在回收站视图上拖放
【发布时间】:2016-10-31 06:55:49
【问题描述】:

我正在尝试将拖放功能实现到 Firebase 回收器视图。文档中没有足够的信息用于此实现。我假设我必须使用 onchildmoved 作为事件监听器,但我不知道如何重新排序数据。

【问题讨论】:

  • 在拖放方面与 Firebase 几乎没有关系。如果您已经在非 Firebase 回收器视图中进行拖放操作,那么添加 Firebase 部件应该不会太难。您是否已经让非 Firebase 部件正常工作?如果是这样,看到这将使这个问题更加具体。
  • 是的,我确实可以使用它,但是在进行拖放时,会附加一个数组列表,然后我必须将所有其他项目向下交换。当涉及到 firebase 时,我只收到一个快照,我不知道如何更改与 firebase 数据库关联的列表的顺序。
  • 您能否将您拥有的代码减少到重现问题所需的最低限度。这样的MCVE 会使它更加具体。

标签: java android firebase-realtime-database


【解决方案1】:

试图让这个在 Firebase UI 3.1.0 上为自己工作时,我有点困惑。这是我的最终工作解决方案(下面的解释):

// overrides from ItemTouchHelper
override fun onMove(recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder): Boolean {
    val fromPos = viewHolder.adapterPosition
    val toPos = target.adapterPosition

    val fromSnapshot = snapshots.first { it.order == fromPos }
    val toSnapshot = snapshots.first { it.order == toPos }

    Logger.d("Habit ${fromSnapshot.name} (${fromSnapshot.order}) to $toPos")
    Logger.d("Habit ${toSnapshot.name} (${toSnapshot.order}) to $fromPos")
    fromSnapshot.order = toPos
    toSnapshot.order = fromPos

    hasDragged = true

    notifyItemMoved(toPos, fromPos)
    return true
}
// overrides from FirebaseRecyclerAdapter
override fun onChildChanged(type: ChangeEventType?, snapshot: DataSnapshot?, newIndex: Int, oldIndex: Int) {
    when (type) {
        ChangeEventType.MOVED -> if (!hasDragged) {
            super.onChildChanged(type, snapshot, newIndex, oldIndex)
        }
        else -> super.onChildChanged(type, snapshot, newIndex, oldIndex)
    }
}

override fun onDataChanged() {
    hasDragged = false
}

我可能做错了什么,但似乎this answer above 上面的代码重复了工作,因为 FirebaseRecyclerAdapter 和可观察的快照将在通知时自动更新。这导致拖动以新值突然结束。通过使用 getSnapshots().get(pos)(kotlin 中的快照 [fromPos]),我也得到了错误的订单号(可能是我的错)。这导致了一些非常奇怪的动画。此外,在移动通知时,我必须反转到/从(目标/viewHolder)位置。但是,请注意,这确实依赖于使用 order 字段在查询中进行排序。最后,如果是因为用户拖动,我不想让 FirebaseRecyclerAdapter#onChildChanged 方法调用,这也会导致不必要的动画和重复工作。

【讨论】:

    【解决方案2】:

    在这里我发布了对我有用的解决方案。它基于@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")
    }
    

    【讨论】:

      【解决方案3】:

      我遇到了与此类似的问题并执行了以下操作(假设您使用的是扩展 ItemTouchHelper.SimpleCallback 的类,例如 RecyclerViewTouchHelper):

      1) 向您的 Firebase 子级添加一个属性以记录其在列表中的顺序(例如 orderNumber)。

      2) 修改RecyclerViewTouchHelperonMove() 方法,使其包含以下内容:

      @Override
      public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
      
          final int firstPosition = viewHolder.getAdapterPosition();
          final int secondPosition = target.getAdapterPosition();
          DatabaseReference firstItemRef = mMovieAdapter.getRef(viewHolder.getAdapterPosition());
          DatabaseReference secondItemRef = mMovieAdapter.getRef(target.getAdapterPosition());
      
          HashMap<String, Object> updateFirstItemOrderNumber = new HashMap<>();
          updateFirstItemOrderNumber.put("orderNumber", secondPosition);
          firstItemRef.updateChildren(updateFirstItemOrderNumber);
      
          HashMap<String, Object> updateSecondItemOrderNumber = new HashMap<>();
          updateSecondItemOrderNumber.put("orderNumber", firstPosition);
          secondItemRef.updateChildren(updateSecondItemOrderNumber);
      
          return false;
      }
      

      3) 创建RecyclerViewAdapter 时,请确保您使用的查询按orderNumber 排序,例如

      Query orderedListQuery = FirebaseRef.orderByChild("orderNumber");
      

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2021-09-29
        • 2016-11-15
        • 1970-01-01
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        • 2017-12-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多