2016-06-28 6 views
10

Firebaseリサイクラビューにドラッグアンドドロップ機能を実装しようとしています。この実装では、ドキュメントに十分な情報がありません。私はイベントリスナーのためにonchildmovedを使用する必要があると仮定していますが、データの並べ替え方法はわかりません。firebaseリアルタイムデータベースを使用してリサイクルビューにドラッグアンドドロップを使用する方法

+0

。 Firebase以外のリサイクラビューで既にドラッグ&ドロップ操作を行っている場合は、Firebaseのパーツを追加するのは難しくありません。あなたは既にFirebase以外の部品を使用していますか?もしそうなら、それを見れば、この質問はより具体的になります。 –

+0

はい、私はそれが動作しているが、ドラッグアンドドロップを行うときに私はその後、他のすべての項目を1つのスポットをスワップする必要がありますarraylistを添付しています。 firebaseになるとスナップショットを受け取るだけです。firebaseデータベースに関連付けられたリストの順序を変更する方法はわかりません。 – Smeekheff

+1

問題を再現するのに必要なコードを最小限に抑えることができますか?そのような[MCVE](http://stackoverflow.com/help/mcve)は、より具体的なものにするでしょう。 –

答えて

1

私はこれと同様の問題を持っていたし、(あなたがItemTouchHelper.SimpleCallback例えばRecyclerViewTouchHelperを拡張するクラスを使用していると仮定して)次のことを行っている:

1)内の順序を記録するために、あなたのFirebase子供にプロパティを追加しますリスト(例えば、 "orderNumber")。

2)以下のものを含むためにあなたのRecyclerViewTouchHelperのonMove()メソッドを変更します。

@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を作成するときに、あなたが使用したクエリは、例えば注文番号で注文していることを確認してください

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

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 
} 

が、FirebaseRecyclerAdapterと観測可能なスナップショットを自動的に通知に更新しますので、上記のコードthis answer aboveは努力を重複していることに思えました。これにより、ドラッグは新しい値で突然終了しました。 getSnapshots()。get(pos)(kotlinのスナップショット[fromPos])を使用して、間違った注文番号(恐らく私の間違い)を取得していました。これは本当に奇妙なアニメーションを引き起こしました。また、移動時に通知するときに、to/from(target/viewHolder)の位置を逆にする必要がありました。ただし、これは注文フィールドを使用したクエリの注文に依存していることに注意してください。最後に、FirebaseRecyclerAdapter#onChildChangedメソッドがユーザーのドラッグのために呼び出すようにしたくない場合、不要なアニメーションと重複した作業も発生します。

1

ここでは私のために働く解決策を掲載します。これは@ prodaeaの答えに基づいていますが、これは正しいアプローチですが、完全に機能していません。 firebaseデータベースの更新が失われており、Firebaseの更新時にアニメーションの中断を避けるためにChangeEventType.CHANGEDに対する追加のチェックがあります。

コードFirebaseRcyclerAdapterを拡張し、単一の方法onItemMove(fromPosition: Int, toPosition: Int): Booleanとのインタフェースを実装するアダプタの内側に住んでいる:それはドラッグ・アンド・ドロップをするとなるとFirebaseにはほとんど関連がある

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") 
} 
関連する問題