Adding Swipe Gestures to RecyclerViews

A big part of Material Design is the way users get to interact with the visual elements of an app. Therefore, in addition to taps and long presses, a well-made Android app today is expected to handle more complex touch such as swipes and drags. This is especially important if the app uses lists to display its data.

By using the RecyclerView widget, and a few other Android Jetpack components, you can handle a wide variety of list-related gestures in your apps. Furthermore, in just a few lines of code, you can associate Material Motion animations with those gestures.

In this tutorial, I’ll show you how to add a few common swipe gestures, complete with intuitive animations, to your lists.

Prerequisites

To be able to make the most of this tutorial, you’ll need:

  • Android Studio 3.2.1 or higher
  • a phone or tablet running Android API level 23 or higher

1. Creating a List

To keep this tutorial short, let’s use one of the templates available in Android Studio to generate our list.

Start by launching Android Studio and creating a new project. In the project creation wizard, make sure you choose the Empty Activity option.

Project creation wizard

Instead of the Support library, we’ll be using Android Jetpack in this project. So, once the project has been generated, go to Refactor > Migrate to AndroidX. When prompted, press the Migrate button.

Confirmation prompt for proceeding with migration

Next, to add a list to the project, go to File > New > Fragment > Fragment (List). In the dialog that pops up, go ahead and press the Finish button without making any changes to the default values.

List fragment creation wizard

At this point, Android Studio will create a new fragment containing a fully configured RecyclerView widget. It will also generate dummy data to display inside the widget. However, you’ll still have to add the fragment to your main activity manually.

To do so, first add the OnListFragmentInteractionListener interface to your main activity and implement the only method it contains.

Next, embed the fragment inside the activity by adding the following  tag to the activity_main.xml file:

At this point, if you run your app, you should be able to see a list that looks like this:

App displaying a simple list

2. Adding the Swipe-to-Remove Gesture

Using the ItemTouchHelper class, you can quickly add swipe and drag gestures to any RecyclerView widget. The class also provides default animations that run automatically whenever a valid gesture is detected.

The ItemTouchHelper class needs an instance of the abstract ItemTouchHelper.Callback class to be able to detect and handle gestures. Although you can use it directly, it’s much easier to use a wrapper class called SimpleCallback instead. It’s abstract too, but you’ll have fewer methods to override.

Create a new instance of the SimpleCallback class inside the onCreateView() method of the ItemFragment class. As an argument to its constructor, you must pass the direction of the swipe you want it to handle. For now, pass RIGHT to it so that it handles the swipe-right gesture.

The class has two abstract methods, which you must override: the onMove() method, which detects drags, and the onSwiped() method, which detects swipes. Because we won’t be handling any drag gestures today, make sure you return false inside the onMove() method.

Inside the onSwiped() method, you can use the adapterPosition property to determine the index of the list item that was swiped. Because we are implementing the swipe-to-remove gesture now, pass the index to the removeAt() method of the dummy list to remove the item.

Additionally, you must pass the same index to the notifyItemRemoved() method of the RecyclerView widget’s adapter to make sure that the item is not rendered anymore. Doing so also runs the default item removal animation.

At this point, the SimpleCallback object is ready. All you need to do now is create an ItemTouchHelper object with it and attach the RecyclerView widget to it.

If you run the app now, you’ll be able to swipe items out of the list.

3. Revealing a Background View

Although the swipe-to-remove gesture is very intuitive, some users may not be sure what happens when they perform the gesture. Therefore, Material Design guidelines say that the gesture must also progressively reveal a view hidden behind the item, which clearly indicates what’s going to happen next. Usually, the background view is simply an icon displaying a trash bin.

To add the trash bin icon to your project, go to File > New > Vector Asset and select the icon named delete.

Icon selection dialog

You can now get a reference to the icon in your Kotlin code by calling the getDrawable() method. So add the following line to the onCreateView() method of the ItemFragment class:

Displaying a view behind a list item is slightly complicated because you need to draw it manually while also making sure that its bounds match the bounds of the region that’s progressively revealed.

Override the onChildDraw() method of your SimpleCallback implementation to start drawing.

In the above code, the call to the onChildDraw() method of the superclass is important. Without it, your list items will not move when they are swiped.

Because we are handling only the swipe-right gesture, the X coordinates of the upper left and bottom left corners of the background view will always be zero. The X coordinates of the upper right and bottom right corners, on the other hand, should be equal to the dX parameter, which indicates how much the list item has been moved by the user.

To determine the Y coordinates of all the corners, you’ll have to use the top and bottom properties of one of the views present inside the viewHolder object.

Using all these coordinates, you can now define a rectangular clip region. The following code shows you how to use the clipRect() method of the Canvas object to do so:

Although you don’t have to, it’s a good idea to make the clip region visible by giving it a background color. Here’s how you can use the drawColor() method to make the clip region gray when the swipe distance is small and red when it’s larger.

You’ll now have to specify the bounds of the trash bin icon. These bounds must include a margin that matches that of the text shown in the list items. To determine the value of the margin in pixels, use the getDimension() method and pass text_margin to it.

You can reuse the coordinates of the clip region’s upper left corner as the coordinates of the icon’s upper left corner. They must, however, be offset by the textMargin. To determine the coordinates of its bottom right corner, use its intrinsic width and height. Here’s how:

Finally, draw the icon by calling its draw() method.

If you run the app again, you should be able to see the icon when you swipe to remove a list item.

4. Adding the Swipe-to-Refresh Gesture

The swipe-to-refresh gesture, also known as the pull-to-refresh gesture, has become so popular these days that Android Jetpack has a dedicated component for it. It’s called SwipeRefreshLayout, and it allows you to quickly associate the gesture with any RecyclerView, ListView, or GridView widget.

To support the swipe-to-refresh gesture in your RecyclerView widget, you must make it a child of a SwipeRefreshLayout widget. So open the fragment_item_list.xml file, add a  tag to it, and move the  tag inside it. After you do so, the file’s contents should like this:

The list fragment assumes that the RecyclerView widget is the root element of its layout. Because this is not true anymore, you need to make a few changes in the onCreateView() method of the ItemFragment class. First, replace the first line of the method, which inflates the layout, with the following code:

Next, change the last line of the method to return the SwipeRefreshLayout widget instead of the RecyclerView widget.

If you try running the app now, you’ll be able to perform a vertical swipe gesture and get visual feedback. The contents of the list won’t change, though. To actually refresh the list, you must associate an OnRefreshListener object with the SwipeRefreshLayout widget.

Inside the listener, you are free to modify the data the list displays based on your requirements. For now, because we’re working with dummy data, let’s just empty the list of dummy items and reload it with 25 new dummy items. The following code shows you how to do so:

After updating the data, you must remember to call the notifyDataSetChanged() method to let the adapter of the RecyclerView widget know that it must redraw the list.

By default, as soon as the user performs the swipe-to-refresh gesture, the SwipeRefreshLayout widget displays an animated progress indicator. Therefore, after you have updated the list, you must remember to remove the indicator by setting the isRefreshing property of the widget to false.

If you run the app now, remove a few list items, and perform the swipe-to-refresh gesture, you’ll see the list reset itself.

Conclusion

Material Design has been around for a few years now, and most users these days expect you to handle many of the gestures it mentions. Fortunately, doing so doesn’t take too much effort. In this tutorial, you learned how to implement two very common swipe gestures. You also learned how to progressively reveal a view hidden behind a list item being swiped.

You might also like
Leave A Reply

Your email address will not be published.