Drag and Swipe in RecyclerView. Part 1: ItemTouchHelper

Original author: Paul Burke
  • Transfer
  • Tutorial

Drag and Swipe in RecyclerView.  Part 1: ItemTouchHelper


There are many educational materials, libraries and examples of drag & drop and swipe-to-dismiss in Android using RecyclerView. Most of them still use the outdated View.OnDragListener and the SwipeToDismiss approach developed by Roman Nurik. Although new and more effective methods are already available. Very few use the latest API, often relying on GestureDetectorsand onInterceptTouchEventor on other more complex implementations. In fact, there is a very simple way to add these functions to RecyclerView. This requires only one class, which is also part of the Android Support Library.


ItemTouchHelper


ItemTouchHelper - is a powerful tool that takes care of everything that needs to be done to add features drag & drop , and swipe-to-dismiss in RecyclerView. This utility is a subclass of RecyclerView.ItemDecoration , making it easy to add to almost any existing LayoutManageradapter. It also works with animation elements and provides the ability to drag items of one type to another place in the list and much more. In this article, I will demonstrate a simple implementation ItemTouchHelper. Later, in this series of articles, we will expand the scope and consider the remaining possibilities.


Note. Want to see the result right away? Check out Github: Android ItemTouchHelper-Demo . The first commit is related to this article. The demo .apkfile can be downloaded here .


Example


Customization


First we need to configure RecyclerView. If you have not done so yet, add the dependency RecyclerViewto your file build.gradle.


compile'com.android.support:recyclerview-v7:22.2.0'

ItemTouchHelperwill work with virtually any RecyclerView.Adapterand LayoutManager, but this article is based on examples using these files .


Using ItemTouchHelper and ItemTouchHelper.Callback


To use ItemTouchHelper, you need to create an ItemTouchHelper.Callback . This is an interface that allows you to track movement ( eng. Move ) and swipe ( eng. Swipe) actions . In addition, here you can control the state of the selected viewcomponent and override the default animation. There is a helper class that you can use if you want to use the basic implementation, SimpleCallback . But in order to understand how this works in practice, we will do everything on our own.


The main interface functions that we must override in order to enable basic drag & drop and swipe-to-dismiss functionality :


getMovementFlags(RecyclerView, ViewHolder)
onMove(RecyclerView, ViewHolder, ViewHolder)
onSwiped(ViewHolder, int)

We will also use several helper methods:


isLongPressDragEnabled()
isItemViewSwipeEnabled()

Consider them one by one.


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

ItemTouchHelpermakes it easy to determine the direction of the event. You need to override the method getMovementFlags()to specify which directions for drag and drop will be supported. To create returned flags, use an auxiliary method ItemTouchHelper.makeMovementFlags(int, int). In this example, we allow dragging and brushing in both directions.


@OverridepublicbooleanisLongPressDragEnabled(){
    returntrue;
}

ItemTouchHelpercan only be used for dragging without the swipe function (or vice versa), so you must specify which features should be supported. The method isLongPressDragEnabled()must return a value truein order to support dragging after a long press on an element RecyclerView. Alternatively, you can call the method ItemTouchHelper.startDrag(RecyclerView.ViewHolder)to start dragging manually. Consider this option later.


@OverridepublicbooleanisItemViewSwipeEnabled(){
    returntrue;
}

To allow swipe after touching anywhere within the view-component, simply return the value truefrom the method isItemViewSwipeEnabled(). Alternatively, you can call a method ItemTouchHelper.startSwipe(RecyclerView.ViewHolder)to start brushing manually.


The following two methods, onMove()and onSwiped(), are required in order to notify about updating data. So, first we will create an interface that allows you to pass these events along a call chain.


ItemTouchHelperAdapter.java


publicinterfaceItemTouchHelperAdapter{
    voidonItemMove(int fromPosition, int toPosition);
    voidonItemDismiss(int position);
}

The easiest way to do this is to have RecyclerListAdapter implement the listener.


publicclassRecyclerListAdapterextendsRecyclerView.Adapter<ItemViewHolder> 
        implementsItemTouchHelperAdapter{
// ... код из [примера](https://gist.github.com/iPaulPro/2216ea5e14818056cfcc#file-recyclerlistadapter-java)@OverridepublicvoidonItemDismiss(int position){
    mItems.remove(position);
    notifyItemRemoved(position);
}
@OverridepublicbooleanonItemMove(int fromPosition, int toPosition){
    if (fromPosition < toPosition) {
        for (int i = fromPosition; i < toPosition; i++) {
            Collections.swap(mItems, i, i + 1);
        }
    } else {
        for (int i = fromPosition; i > toPosition; i--) {
            Collections.swap(mItems, i, i - 1);
        }
    }
    notifyItemMoved(fromPosition, toPosition);
    returntrue;
}

It is very important to call the methods notifyItemRemoved()and notifyItemMoved()have the adapter see the changes. It should also be noted that we change the position of the element each time the viewcomponent moves to a new index, and not at the very end of the move (the “drop” event) .


Now we can go back to creation SimpleItemTouchHelperCallback, since we still need to override the onMove()and methods onSwiped(). First add a constructor and a field for the adapter:


privatefinal ItemTouchHelperAdapter mAdapter;
publicSimpleItemTouchHelperCallback(
        ItemTouchHelperAdapter adapter){
    mAdapter = adapter;
}

Then redefine the remaining events and report this to the adapter:


@OverridepublicbooleanonMove(RecyclerView recyclerView, 
        RecyclerView.ViewHolder viewHolder, 
        RecyclerView.ViewHolder target){
    mAdapter.onItemMove(viewHolder.getAdapterPosition(), 
            target.getAdapterPosition());
    returntrue;
}
@OverridepublicvoidonSwiped(RecyclerView.ViewHolder viewHolder, 
        int direction){
    mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}

As a result, the class Callbackshould look like this:


publicclassSimpleItemTouchHelperCallbackextendsItemTouchHelper.Callback{
    privatefinal ItemTouchHelperAdapter mAdapter;
    publicSimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter){
        mAdapter = adapter;
    }
    @OverridepublicbooleanisLongPressDragEnabled(){
        returntrue;
    }
    @OverridepublicbooleanisItemViewSwipeEnabled(){
        returntrue;
    }
    @OverridepublicintgetMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder){
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    }
    @OverridepublicbooleanonMove(RecyclerView recyclerView, ViewHolder viewHolder, 
            ViewHolder target){
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        returntrue;
    }
    @OverridepublicvoidonSwiped(ViewHolder viewHolder, int direction){
        mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
    }
}

When the callback is ready, we can create ItemTouchHelperand call a method attachToRecyclerView(RecyclerView)(for example, in MainFragment.java ):


ItemTouchHelper.Callback callback = 
    new SimpleItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);

After the launch should get something like this:


Result


Conclusion


This is the most simplified implementation ItemTouchHelper. However, you may notice that you do not need to use a third-party library to implement standard actions drag & drop , and swipe-to-dismiss in RecyclerView. In the next part, we will pay more attention to the appearance of the elements at the time of dragging or swiping.


Source


I created a project on GitHub to demonstrate what is described in this series of articles: Android-ItemTouchHelper-Demo . The first commit mainly relates to this part and a little to the second .


→ Drag and Swipe in RecyclerView. Part 2: Drag and Drop Controllers, Grid, and Custom Animations


Also popular now: