Drag and Swipe in RecyclerView. Part 1: ItemTouchHelper
- Transfer
- Tutorial
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 GestureDetectors
and onInterceptTouchEvent
or 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 LayoutManager
adapter. 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 .apk
file can be downloaded here .
Customization
First we need to configure RecyclerView
. If you have not done so yet, add the dependency RecyclerView
to your file build.gradle
.
compile'com.android.support:recyclerview-v7:22.2.0'
ItemTouchHelper
will work with virtually any RecyclerView.Adapter
and 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 view
component 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);
}
ItemTouchHelper
makes 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;
}
ItemTouchHelper
can 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 true
in 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 true
from 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.
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 view
component 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 Callback
should 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 ItemTouchHelper
and 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:
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