Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android - how to catch Drop action of ItemTouchHelper which is used with RecyclerView

I have a problem with ItemTouchHelper of RecyclerView.

I am making a game. The game board is actually a RecyclerView. RecyclerView has GridLayoutManager with some span count. I want to implement drag & drop recyclerview's items. Any item can dragging over all directions (up, down, left, right).

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, why ItemTouchHelper's onMoved function works when I still dragging item on the RecyclerView ? How can I achieve this ?

like image 566
okarakose Avatar asked Mar 10 '16 15:03

okarakose


2 Answers

While dragging and dropping an item, the onMove() can be called more than once, but the clearView() will be called once. So you can use this to indicate the drag was over(drop was happened). And use two variables dragFrom and dragTo to trace the really position in a completed "drag & drop".

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) was like below:

public void onItemMove(int fromPosition, int toPosition) {
    list.add(toPosition, list.remove(fromPosition));
    notifyItemMoved(fromPosition, toPosition);
}
like image 170
lang Avatar answered Oct 15 '22 03:10

lang


The onSelectedChanged(RecyclerView.ViewHolder, int) callback provides information about the current actionState:
- ACTION_STATE_IDLE:
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE

So you could keep track whether the order changed, and when the state changes to ACTION_STATE_IDLE, you can do what you need to do!

Example:

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;
    }
}
like image 12
Timon Langlotz Avatar answered Oct 15 '22 03:10

Timon Langlotz