Android RecyclerView – Drag and Drop and Swipe to Dismiss

In the last post I explained how you can implement an expandable recyclerview. In this post we will see how to implement swipe-to-remove and drag-and-drop gestures.

RecyclerView provides a built-in mechanism to enable drag and drop and swipe to dismiss gestures. This is a great advantage for Recyclerview compared to ListView where we had to write all the boilerplate for animating items for dragging and swiping. So if you are still using ListView this is a great feature for you to switch to RecyclerView.

This can be accomplished using the ItemTouchHelper class provided with RecyclerView. This class does all the heavy lifting needed for handling swiping and dragging and animating the view accordingly.

You can specify in which directions and in which ViewHolders the gestures should work. Also you need to be notified when a swipe or drag and drop gesture is completed. This can be addressed using the ItemTouchHelper.Callback class.

To quickly setup these gestures, we can subclass the Callback class and override three methods: getMovementFlags(), onMove() and onSwiped().

  • In getMovementFlags() you have to return a int value. This value denotes a composite flag that defines the movement directions for each movement states namely IDLE, DRAG and SWIPE. This method takes two parameters: a RecyclerView instance and the ViewHolder of the view. You can return the flag using the method makeFlag() or the convenience method makeMovementFlags(). In the below code, drag supports both up and down directions, and swipe supports left and right directions.
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
    return makeMovementFlags(dragFlags, swipeFlags);
}
  • onMove() gets called when a view is dragged from its position to other positions. You have to return true if the item has been moved from its old position to a new position. Here you can notify the adapter about the position change. By default an item can be moved only after long pressing it.
  • Similarly, onSwipe() gets called when a view is completely swiped out. Here you can notify the adapter about the removal.

To correctly handle drag-and-drop and swipe, we can create a interface.

public interface ActionCompletionContract {
    void onViewMoved(int oldPosition, int newPosition);
    void onViewSwiped(int position);
}

And let’s make our Adapter implement this. In the onViewMoved() callback, we will remove the data at the oldPosition and add it to the newPosition, and notify the adapter:

@Override
public void onViewMoved(int oldPosition, int newPosition) {
    User targetUser = usersList.get(oldPosition);
    User user = new User(targetUser);
    usersList.remove(oldPosition);
    usersList.add(newPosition, user);
    notifyItemMoved(oldPosition, newPosition);
}

For swipe to dismiss action, we call onViewSwiped() interface callback and remove the item:

@Override
public void onViewSwiped(int position) {
    usersList.remove(position);
    notifyItemRemoved(position);
}

Now to call these appropriate callbacks from the ItemTouchHelper.Callback class, we will pass the adapter to the class:

SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(swipeAndDragHelper);

Here I have created a subclass of the ItemTouchHelper.Callback called SwipeAndDragHelper.

And finally to integrate this ItemTouchHelper with our RecyclerView, we call attachToRecyclerView() method:

touchHelper.attachToRecyclerView(userRecyclerView);

That’s it. We have implemented the drag-and-drop and swipe-to-dismiss gestures. This is how it looks:

recyclerview-itemtouchhelper

 

Now what if we want to move the items only by touching a handle something like below:

components-listcontrols-reorder
Source: Material Design Guidelines

For that, the ItemTouchHelper provides startDrag() and startSwipe() methods to manually start drag and swipe actions respectively. Let’s implement this.

First to manually drag, we must disable the default dragging. Since default dragging is started when a view is long pressed, we must disable it. This can be done by returning false in isLongPressEnabled() of the Callback class.

Then pass the instance of ItemTouchHelper to the adapter. Then implement onTouchListener for the reorder handle ImageView. Inside onTouch call the startDrag method passing the ViewHolder as parameter like below:

In onBindViewHolder:

((UserViewHolder) holder).reorderView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            touchHelper.startDrag(holder);
        }
        return false;
    }
});

Result:

recyclerview-item-touch-helper-reorder

 

Next we will add some fade effect to the swipe action. Right now when the view gets swiped there is no effect except the view gets transitioned in x direction.

The Callback class provides onChildDraw() method to draw anything over the area of the child view being swiped or dragged. It provides a canvas, viewholder, x and y displacement caused by the gesture, and action state as parameters among others. So we will check the action state and if it is equal to ACTION_STATE_SWIPE we will reduce the alpha of the view as it moves away from its original position.

@Override
public void onChildDraw(Canvas c,
                        RecyclerView recyclerView,
                        RecyclerView.ViewHolder viewHolder,
                        float dX,
                        float dY,
                        int actionState,
                        boolean isCurrentlyActive) {
    if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
        float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth());
        viewHolder.itemView.setAlpha(alpha);
    }
    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}

Now you will get a nice fade as below when swiping:

recyclerview-swipe-dismiss-item-fade-out

Checkout the full source code here.

If you have feedback, comment below.

12 thoughts on “Android RecyclerView – Drag and Drop and Swipe to Dismiss

  1. Thanks for your post! Wonderful code. If only it had methods to disallow dropping into/under certain sections of the list…

    I see the list’s re-arrangement can be prevented by checking if newPosition in onViewMoved() is within non-droppable region – but how to trigger something like fly-back animation instead of drop?

    Like

    1. You can make onViewMoved() return boolean. So if you don’t want to drop in a section, return false. Then in onMove() of the Callback class if onViewMoved() returns false, return false for onMove(). Also the view goes back to the previous position with a default fly animation.

      Like

  2. I apprechiate that you show us your code but seriouse how should anyone follow this tutorial who is new to this? You show some piece of code without really explaining it and saying where it belongs. other that that, i feel like you left out some pieces here. I tried to do this with the full code on github. Now my Problem is, when you initialize the the DragAndSwipeHelper with:

    SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper();

    it takes a ActionCompletionContract, idk what it is but you give it your RecyclerView Adapter.This does not work for me it says ActionCompletionContrat cannot be applied to RecyclerView.Adapter.
    And btw where do you create the Constructor of SwipeAndDragHelper? I only saw it in the resource code…

    Liked by 1 person

    1. Hello Jonas. Thanks for the feedback. I will definitely make the post more clear.

      Now to answer your questions, ActionCompletionContract is an interface to notify the adapter about the move and swipe actions. So the adapter needs to implement this interface. Now we pass the adapter as parameter for SwipeAndDragHelper since it implements the interface.

      The SwipeAndDragHelper is created in the activity’s onCreate() method. Refer this https://github.com/sjthn/RecyclerViewDemo/blob/advanced-usecases/app/src/main/java/com/example/srijith/recyclerviewdemo/UserListActivity.java

      Liked by 1 person

  3. Thank you very much for sharing your code! It is really great!!! Is there a way to read the list, to know the order of the elements in case the user modified the initial list? Thank you in advance mate!

    Like

    1. Thanks for your post. It is very helpful to me. But I’m having a problem. My project get data from the database and it have a category field in Item class. So how do you build a recyclerview with type and move items from one group to group another? And how do known this item belongs to which group after move? My english not good, sorry very much. Thank you for reading.

      Like

Leave a comment