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 gestures 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 swipe 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.

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.

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.

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.

1234override fun onListFragmentInteraction(item: DummyContent.DummyItem?) {// leave empty }

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

1234<fragment android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/list_fragment"android:name="com.tutsplus.rvswipes.ItemFragment" />

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

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.

123456val myCallback = object: ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { // More code here }

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.

010203040506070809101112override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder): Boolean = false override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { // More code here }

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.

1DummyContent.ITEMS.removeAt(viewHolder.adapterPosition)

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.

1adapter?.notifyItemRemoved(viewHolder.adapterPosition)

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.

12val myHelper = ItemTouchHelper(myCallback)myHelper.attachToRecyclerView(this)

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.

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.

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 RecyclerViewListView, 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 <SwipeRefreshLayout> tag to it, and move the <RecyclerView> tag inside it. After you do so, the file’s contents should like this:

0102030405060708091011121314151617<?xml version="1.0" encoding="utf-8"?><androidx.swiperefreshlayout.widget.SwipeRefreshLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/list"android:name="com.tutsplus.rvswipes.ItemFragment"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginLeft="16dp"android:layout_marginRight="16dp"/> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

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:

123456val srLayout: SwipeRefreshLayout = inflater.inflate(R.layout.fragment_item_list, container, false) as SwipeRefreshLayout val view = srLayout.findViewById<RecyclerView>(R.id.list)

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

1return srLayout

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.

123srLayout.setOnRefreshListener {// More code here    }

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:

123456DummyContent.ITEMS.clear()for(i in 1..25) {DummyContent.ITEMS.add(DummyContent.DummyItem("$i", "Item $i", ""))}

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.

1view.adapter?.notifyDataSetChanged()

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.

1srLayout.isRefreshing = 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.