How to Add Multiple Selection to Android RecyclerView

The RecyclerView widget is an integral part of most Android applications today. Ever since it was added to the Android support library in late 2014, it has eclipsed the ListView widget as the most preferred widget for displaying large, complex lists. However, there’s one important feature missing in it: support for selecting and tracking list items. RecyclerView Selection, an addon library Google released in March this year, tries to fix this.

In this tutorial, I’ll show you how to use the new library to create an app that offers an intuitive interface for selecting multiple items in a list. Follow along with this Android RecyclerView multiple selection example, and you’ll learn some skills you can apply in your own apps.

Prerequisites

To follow along, you’ll need:

  • the latest version of Android Studio
  • a device or emulator running Android API level 23 or higher

1. Add RecyclerView Android Dependencies

To add the RecyclerView Selection library to your Android Studio project, mention the following implementation dependencies in your app module’s build.gradle file:

2. Create a List

Throughout this tutorial, we’ll be working with a small list of items, each containing a person’s name and phone number.

To store the data of each list item, create a Kotlin data class called Person and add two properties to it: name and phone.

You can now go ahead and create a list of Person objects in your main activity.

3. Add a Recycler View to the Layout

We will, of course, be using a RecyclerView widget to display the list. So add a <RecyclerView> tag to your main activity’s layout XML file.

To specify the layout of the list items, create a new XML file and call it list_item.xml. Inside it, add two TextView widgets: one to display the name, and the other to display the phone number. If you use a LinearLayout element to position the widgets, the contents of the XML file should look like this:

4. Create a View Holder

You can think of a view holder as an object that contains references to the views present in the layout of the list items. Without it, the RecyclerView widget will not be able to render the list items efficiently.

For now, you need a view holder that holds the two TextView widgets you created in the previous step. So create a new class that extends the RecyclerView.ViewHolder class, and initialize references to both the widgets inside it. Here’s how:

Additionally, the RecyclerView Selection addon needs a method it can call to uniquely identify selected list items. This method ideally belongs to the view holder itself. Furthermore, it must return an instance of the ItemDetailsLookup.ItemDetails class. Therefore, add the following code to the view holder:

You must now override two abstract methods present in the ItemDetails class. Start by overriding the getPosition() method and returning the adapterPosition property of the view holder inside it. The adapterPosition property is usually nothing but the index of a list item.

Next, override the getSelectionKey() method. This method must return a key that can be used to uniquely identify a list item. To keep things simple, let’s just return the itemId property of the view holder.

You are free to use any other technique for generating the selection key, so long as it generates unique values.

5. Handle User Touches

For the RecyclerView Selection addon to work correctly, whenever the user touches the RecyclerView widget, you must translate the coordinates of the touch into an ItemDetails object.

Create a new class that extends the ItemDetailsLookup class, and add a constructor to it that can accept the RecyclerView widget as an argument. Note that, because the class is abstract, Android Studio will automatically generate a stub for its abstract method.

As you can see in the above code, the getItemDetails() method receives a MotionEvent object. By passing the event’s X and Y coordinates to the findChildViewUnder() method, you can determine the view associated with the list item the user touched. To convert the View object into an ItemDetails object, all you need to do is call the getItemDetails() method. Here’s how:

6. Create an Adapter

You’ll now need an adapter that can bind your list to your RecyclerView widget. To create one, create a new class that extends the RecyclerView.Adapter class. Because the adapter needs access to the list and your activity’s context, the new class must have a constructor that can accept both as arguments.

It is important that you explicitly indicate that each item of this adapter is going to have a unique stable identifier that is of type Long. The best place to do so is inside an init block.

Additionally, to be able to use the position of an item as its unique identifier, you’ll have to override the getItemId() method.

Because the RecyclerView.Adapter class is abstract, you’ll now have to override three more methods to make your adapter usable.

First, override the getItemCount() method to return the size of the list.

Next, override the onCreateViewHolder() method. This method must return an instance of the view holder class you created earlier in this tutorial. To create such an instance, you must call the constructor of the class and pass the inflated layout of your list items to it. To inflate the layout, use the inflate() method of the LayoutInflater class. Here’s how:

Lastly, override the onBindViewHolder() method and appropriately initialize the text property of both the TextView widgets present in the view holder.

7. Display the List

At this point, you have almost everything you need to render your list. However, you must still specify how you want the list items to be positioned. For now, let’s position them one below the other using a LinearLayoutManager instance.

For optimal performance, I suggest you also indicate that the size of the RecyclerView widget is not going to change during runtime.

Add the following code to your main activity:

Finally, assign a new instance of your adapter to the adapter property of the RecyclerView widget.

If you run your app now, you’ll be able to see the list.

App displaying list

8. Create a Selection Tracker

The RecyclerView widget still doesn’t allow you to select any items. To enable multi-item selection, you’ll need a SelectionTracker object in your activity.

You can initialize the tracker using the SelectionTracker.Builder class. To its constructor, you must pass a selection ID, your RecyclerView widget, a key provider, your item details lookup class, and a storage strategy.

You are free to use any string as a selection ID. As a key provider, you can use an instance of the StableIdKeyProvider class.

The RecyclerView Selection library offers a variety of storage strategies, all of which ensure that selected items are not deselected when the user’s device is rotated or when the Android system closes your app during a resource crunch. For now, because the type of your selection keys is Long, you must use a StorageStrategy object of type Long.

Once the Builder is ready, you can call its withSelectionPredicate() method to specify how many items you want to allow the user to select. In order to support multi-item selection, as an argument to the method, you must pass the SelectionPredicate object returned by the createSelectAnything() method.

Accordingly, add the following code inside your activity’s onCreate() method:

To make the most of the storage strategy, you must always try to restore the state of the tracker inside the onCreate() method.

Similarly, you must make sure you save the state of the tracker inside your activity’s onSaveInstanceState() method.

The selection tracker is not very useful unless it is associated with your adapter. Therefore, pass it to the adapter by calling the setTracker() method.

The setTracker() method doesn’t exist yet, so add the following code inside your adapter class:

If you try running your app at this point, you will be able to select items in the list. When you enter the multi-item selection mode by long-pressing a list item, you’ll be able to feel a brief vibration on most devices. However, because the selected items are currently indistinguishable from the unselected ones, you’ll have no visual feedback. To fix this, you need to make a few changes inside the onBindViewHolder() method of your adapter.

The conventional way to highlight selected items is to change their background color. Therefore, you must now change the background color of the LinearLayout widget that’s present in your items’ layout XML file. To get a reference to it, get a reference to the parent of one of the TextView widgets available in the view holder.

Add the following code just before the end of the onBindViewHolder() method:

Next, you can call the isSelected() method of the SelectionTracker object to determine whether an item is selected or not.

The following code shows you how to change the background color of selected items to cyan:

If you run the app now, you should be able to see the items you select.

App displaying list with selected items

9. Create a Selection Observer

Usually, you’d want to show the user how many items are currently selected. With the RecyclerView Selection library, doing so is very easy.

Associate a SelectionObserver object with your selection tracker by calling the addObserver() method. Inside the onSelectionChanged() method of the observer, you can detect changes in the number of items selected.

How you display the number of selected items is up to you. For now, I suggest you display the number directly in the action bar of your activity. Optionally, you can also change the background color of the action bar to let the user know that there is an active selection in the list. The following code shows you how:

If you run the app again, you should now see the title change to reflect the number of list items you select.

App displaying count of selected items

Conclusion

In this tutorial, you learned how to use the RecyclerView Selection addon library to add simple item selection support to a RecyclerView widget. You also learned how to dynamically alter the looks of selected items so that users can tell them apart from unselected ones.

To learn more about the library, refer to the official documentation.