webentwicklung-frage-antwort-db.com.de

android - wie man die Drop-Aktion von ItemTouchHelper abfängt, die mit RecyclerView verwendet wird

Ich habe ein Problem mit ItemTouchHelper von RecyclerView .

Ich mache ein Spiel. Das Spielbrett ist eigentlich eine RecyclerView. RecyclerView hat GridLayoutManager mit etwas Spannweite. Ich möchte die Elemente von Drag & Drop Recyclerview implementieren. Jedes Element kann über alle Richtungen gezogen werden (nach oben, unten, links, rechts).

private void initializeLayout() {
    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutFrozen(true);
    recyclerView.setNestedScrollingEnabled(false);

    // set layout manager
    GridLayoutManager layoutManager = new GridLayoutManager(getContext(), BOARD_SIZE,
        LinearLayoutManager.VERTICAL, true);
    recyclerView.setLayoutManager(layoutManager);

    // Extend the Callback class
    ItemTouchHelper.Callback itemTouchCallback = new ItemTouchHelper.Callback() {

    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        Log.w(TAG, "onMove");
        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        // Application does not include swipe feature.
    }

    @Override
    public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                        int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
        Log.d(TAG, "onMoved");
        // this is calling every time, but I need only when user dropped item, not after every onMove function.
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }
    };

    ItemTouchHelper touchHelper = new ItemTouchHelper(itemTouchCallback);
    touchHelper.attachToRecyclerView(recyclerView);
}

SO, warum funktioniert die onMoved-Funktion von ItemTouchHelper, wenn ich das Element immer noch in die RecyclerView ziehe? Wie kann ich das erreichen?

22
okarakose

Beim Ziehen und Ablegen eines Elements kann onMove () mehrmals aufgerufen werden, ClearView () wird jedoch einmal aufgerufen. Sie können dies verwenden, um anzuzeigen, dass das Ziehen beendet wurde (das Ablegen wurde durchgeführt). Verwenden Sie die beiden Variablen dragFrom und dragTo, um die tatsächliche Position in einem abgeschlossenen "Drag & Drop" zu verfolgen.

private ItemTouchHelper.Callback dragCallback = new ItemTouchHelper.Callback() {

    int dragFrom = -1;
    int dragTo = -1;

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return makeMovementFlags(ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT,
                0);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        int fromPosition = viewHolder.getAdapterPosition();
        int toPosition = target.getAdapterPosition();


        if(dragFrom == -1) {
            dragFrom =  fromPosition;
        }
        dragTo = toPosition;

        adapter.onItemMove(fromPosition, toPosition);

        return true;
    }

    private void reallyMoved(int from, int to) {
        // I guessed this was what you want...
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        if(dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
            reallyMoved(dragFrom, dragTo);
        }

        dragFrom = dragTo = -1;
    }

};

adapter.onItemMove (fromPosition, toPosition) war wie folgt:

list.add(toPosition, list.remove(fromPosition)); notifyItemMoved(fromPosition, toPosition);

62
lang

Der onSelectedChanged(RecyclerView.ViewHolder, int)-Callback liefert Informationen zum aktuellen actionState:
- ACTION_STATE_IDLE:
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE 

Sie können also nachverfolgen, ob sich die Reihenfolge geändert hat und wenn sich der Status in ACTION_STATE_IDLE ändert, können Sie tun, was Sie tun müssen!

Beispiel:

private final class MyCallback extends ItemTouchHelper.Callback {
    private boolean mOrderChanged;

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        // Check if positions of viewHolders correspond to underlying model, and if not, flip the items in the model and set the mOrderChanged flag
        mOrderChanged = true;
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
    super.onSelectedChanged(viewHolder, actionState);

    if (actionState == ItemTouchHelper.ACTION_STATE_IDLE && mOrderChanged) {
        doSomething();
        mOrderChanged = false;
    }
}
4
Timon Langlotz

Sie müssen den OnMove-Listener in Ihrem Adapter erkennen:

Collections.swap(youCoolList, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition);

wie dieser Mann https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.blviq6jxp

beispiel für ein spezielles Raster https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.xb74uu7ke

2
LamaUltramarine

Ich habe einige Tests gemacht und onSelectedChanged(RecyclerView.ViewHolder?, Int) schien mir am zuverlässigsten zu sein, um das Ende der Geste (Drop) zu erkennen. Die Methode wird aufgerufen, wenn ein Element gezogen und der Aktionsstatus von ACTION_STATE_DRAG übergeben wird. Wenn das Ziehen beendet ist, wird es mit dem Aktionsstatus ACTION_STATE_IDLE aufgerufen. 

Siehe meine Lösung unten. Der onItemDrag(Int, Int)-Callback wird zum Umordnen von Elementen in einem Adapter verwendet, wenn das Element gezogen wird. Andererseits ist der Callback onItemDragged(Int, Int) dazu gedacht, Positionen in einer Datenbank am Ende der Geste zu aktualisieren.

class ItemGestureHelper(private val listener: OnItemGestureListener) : ItemTouchHelper.Callback() {

    interface OnItemGestureListener {

        fun onItemDrag(fromPosition: Int, toPosition: Int): Boolean

        fun onItemDragged(fromPosition: Int, toPosition: Int)

        fun onItemSwiped(position: Int)
    }

    private var dragFromPosition = -1
    private var dragToPosition = -1

    // Other methods omitted...

    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        // Item is being dragged, keep the current target position
        dragToPosition = target.adapterPosition
        return listener.onItemDrag(viewHolder.adapterPosition, target.adapterPosition)
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        listener.onItemSwiped(viewHolder.adapterPosition)
    }

    override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
        super.onSelectedChanged(viewHolder, actionState)

        when (actionState) {
            ItemTouchHelper.ACTION_STATE_DRAG -> {
                viewHolder?.also { dragFromPosition = it.adapterPosition }
            }
            ItemTouchHelper.ACTION_STATE_IDLE -> {
                if (dragFromPosition != -1 && dragToPosition != -1 && dragFromPosition != dragToPosition) {
                    // Item successfully dragged
                    listener.onItemDragged(dragFromPosition, dragToPosition)
                    // Reset drag positions
                    dragFromPosition = -1
                    dragToPosition = -1
                }
            }
        }
    }

}

0
pgiecek