The RecyclerView
We won’t discuss, in any detail, how to use a RecyclerView.
Check out this tutorial, Android’s RecyclerView and CardView widgets, if you need to know more about using the RecyclerView in your app?
Here are the basics on using a RecyclerView:
- Get a reference to the RecyclerView
-
Add a listener to it so that we can react to the user’s touch
- Get an adapter to feed our list of ItemData to the RecyclerView
- Set the adapter on our RecyclerView
- Set the layout manager for our RecyclerView to manage the positioning and recycling of views within the RecyclerView
The RecyclerView Adapter
We pass the data that we want to display to the adapter. The adapter feeds this data to the RecyclerView, managing the positioning and recycling of views within the RecyclerView.
We’ve created our own custom RecyclerView Adapter, MyRecyclerViewAdapter and instantiate it in the MainActivity, passing its constructor the activity’s context as well as our data, a list of ItemData containing an id and associated text string.
We use the adapter to bind our data to the views displayed in the RecyclerView.
Want to blow your mind? Check out this tutorial, Android Adapters: What are Adapters?
Our custom RecyclerView adapter uses a custom view holder to display each item in a single row.
The Custom ViewHolder
Here’s the custom view holder’s code:
The view holder contains the views for each item that is displayed in a row
The CustomViewHolder contains the views for each item. Each item is displayed in a row and contains three text views:
- itemIdTextView – displays an id for the item
- itemTextView – displays a line of text for each item
- choiceModeTextView – displays the green asterisk (*) indicating that the RecyclerView list is now in action mode and the user can make multiple selections
Creating the view holder: onCreateViewHolder()
We create a view holder, which will contain one of our data items, in the adapter’s onCreateViewHolder() method. This is where we inflate the layout for each of the displayed rows.
The view holder will be reused for each of the rows.
Here’s the row_layout.xml file:
The row_layout.xml layout file is inflated in the adapter’s onCreateViewHolder() method. We use it to display each row in the RecyclerView
Putting the data in the view: onBindViewHolder()
This is where all the magic happens.
The RecyclerView calls the onBindViewHolder() method to display a row at the position passed as a parameter. It replaces the current item in the view holder with the one specified by position (the item of data in the adapter at the given adapter position).
So here’s a breakdown of the onBindViewHolder() methods’ code:
- Get the data at the given adapter position out of our ItemData list and set these values in the view holders’ text views
- Next, call setItemUnSelected(holder) to make sure that all of the displayed itemTextView views (each row) is deselected (displayed as black text)
Here’s the next bit of the onBindViewHolder() methods’ code:
This code is at the heart of our action mode. It takes care of changing the selected items text colour as well as displaying the action mode indicator
So what’s happening here?
- We check whether the list of selected items contains selected ids. If it does:
- Then we make sure that the green action mode indicator is showing by calling setViewIndicatingChoiceMode(holder)
- Then we check if the item that we are about to display is in the selected item list:
- If it is then we set its text colour to red by calling setItemSelected()
- If not then we do nothing (we leave the text colour black)
- If there are no selected items then we make sure that the action mode indicator is not showing by calling setViewIndicatingNormalMode(holder)
The action mode indicator: setViewIndicatingChoiceMode(holder)
This method gets a reference to the choiceModeIndicator text view and makes it visible. This lets the user know that they can now make multiple selections.
Show the user that the app is in action mode by calling setViewIndicatingChoiceMode()
Marking an item as selected: setItemSelected(holder)
Call setItemSelected() to mark an item as selected.
This method gets a reference to the itemText TextView and sets its colour to red.
Call setItemSelected() to mark an item as selected
Unselecting an item
Call setItemUnselected() to deselect a selected item.
This method simply sets the selected TextView’s colour to black.
So how do we go about selecting items?
Check out our and apps which copy Google’s Photos app’s way of showing selections:
Deselected items
Selected items
The image size is reduced by adding padding. An empty circle displays indicating that we’re in action mode and the selected item’s circle includes a blue tick
And here’s a simple version of how we did it in this tutorial:
Deselected item
Selected item
A green asterisk (*) displays, indicating that we’re in action mode and the selected item’s text colour changes to red
Listening for the user’s touch: The listener
We need to listen for when the user touches the text in one of the RecyclerView rows.
To do this, we create a custom touch listener, MyRecyclerViewOnItemTouchListener which implements the RecyclerView.OnItemTouchListener class, which allows us to do this.
Add the custom listener to the RecyclerView
Add our custom listener to our RecyclerView
Our custom listener has three required methods:
The RecyclerView.OnItemTouchListener’s required methods
We’re only interested in the onInterceptTouchEvent() method which listens for all touch events.
The onInterceptTouchEvent() method intercepts the touch and passes it on to our custom listener’s gesture detector’s onTouchEvent() method to process.
The gesture detector
The gesture detector detects gestures and motion events from the event that is passed to it via the listener’s onInterceptTouchEvent() method .
We’re using the support library so we use the GestureDetectorCompat class for our gesture detector, gestureDetectorCompat.
The gesture detector’s onTouchEvent() method analyses the touch event and passes it on to its listener’s appropriate callback method.
Our gesture detector uses the SimpleOnGestureListener() which has a number of callback methods. We’re really only interested in two of them:
- onSingleTapConfirmed() - which is triggered by single touches
- onLongPress() - which is triggered by long-presses
Selecting an item for the first time: making that first selection
Long-pressing an item for the first time puts the RecyclerView into action mode, allowing the user to make multiple selections.
This is what happens when the user selects an item for the first time:
- The app enters action mode
- The toolbar changes into an action mode toolbar
- The Save action menu item icon replaces the overflow menu
Nothing happens if the user touches an item if the app is not in action mode.
So what happens in the onLongPress() method?
Long-pressing an item for the first time:
- The RecyclerView.OnItemTouchListener’s onInterceptTouchEvent() method grabs the touch event and passes it on to the gestureDetectorCompat.onTouchEvent(e) method
- The gesture detector recognises the event as a long press and triggers the SimpleOnGestureListener() callback, onLongPress() method
Let’s have a look at the code:
Long-pressing an item for the first time enables action mode
So what’s happening here?
- isInChoiceMode – the app is in normal mode by default (the normal toolbar is showing and nothing is selected) and isInChoiceMode is set to false. We now set isInChoiceMode to true. Nothing will happen from now on if the user long-presses an item
- showActionModeToolbar() - we show the action mode toolbar by calling showActionModeToolbar()
- findChildViewUnder() – we get the selected view contained in the touch event
- setHapticFeedbackEnabled() and performHapticFeedback() ensures that the device vibrates and emits a sound when the view is touched. We use the LONG_PRESS flag so that the feedback is triggered on a long press
- getChildAdapterPosition() – gets the position of the selected item in the adapter. We’ll use this to find the selected item in our list of items supplied to the adapter
- we use an if statement to check whether it’s a valid position
- addItemIdToSelectedList() – we then add the selected item to the selected list of items (see more on the addItemIdToSelectedList())
- notifyDataSetChanged() – we call notifyDataSetChanged() to force the RecyclerView to rebind and relayout its views. This changes the selected text colour and displays the green asterisk indicating that the app is now in action mode
- updateToolbar() – updates the selected items counter in the toolbar
- The user can now make more selections or deselect selected items by single-touching an item. Long-pressing will not work as isInChoiceMode is true
So what happens when the user single-taps an item? The onSingleTapConfirmed() method
Single-touching an item selects or deselects it.
Here’s the code:
Single-touching an item selects or deselects it
So what’s happening here?
- isInChoiceMode – single-touch will only work if isInChoiceMode is true
- findChildViewUnder() – we get the selected view contained in the touch event
- setHapticFeedbackEnabled() and performHapticFeedback() ensures that the device vibrates and emits a sound when the view is touched. We use the FLAG_IGNORE_VIEW_SETTING flag so that the feedback is triggered when the view is touched, no matter what the current feedback settings are for that view
- getChildAdapterPosition() – gets the position of the selected item in the adapter. We’ll use this to find the selected item in our list of items supplied to the adapter
- We use an if statement to check whether it’s a valid position
- addItemIdToSelectedList() – we then add the selected item to the selected list of items (see more on the addItemIdToSelectedList())
- notifyDataSetChanged() – we call notifyDataSetChanged() to force the RecyclerView to rebind and relayout its’ views. This changes the selected text colour and displays the green asterisk indicating that the app is now in action mode
- updateToolbar() – updates the selected items counter in the toolbar
- The user can now make more selections or deselect selected items by single-touching an item. Long-pressing will not work as isInChoiceMode is true
- Deselecting the last selected item disables the action mode
Adding an item to the list of selected items: The addItemIdToSelectedList() method
The selectedItemIdList list contains the ids of the selected items.
We use the selectedItemIdList list in the RecyclerView adapter’s onBindViewHolder() method to:
- set the action mode indicator (the green asterisk) to show the user that we are in action mode
- change the selected items’ text colour to indicate whether or not it has been selected
We use the addItemIdToSelectedList() method to add or remove the selected item’s id to the selectedItemIdList list.
Here’s the code:
Call addItemIdToSelectedList() method to add or remove selected items
So what happens here?
- We pass this addItemIdToSelectedList() method the selected items’ id
- Then we check whether or not the id is already in our selected items list of ids, adding the id to the list if it’s not already in the list
- If the id is already in the list, then we need to remove it as the user has deselected it
- Finally, if the list is empty then we need to exit the action mode and show the normal toolbar. We do this by setting isInChoiceMode to false and calling showNormalToolbar()
Updating the selected items counter: The updateToolbar() method
The updateToolbar() method updates the selected item counter displayed in the toolbar.
Update the counter by making the TextViews visible and then display the size of the selected items counter list
So what’s happening here?
- selectedItemsCounter – get the size of the selected items list. This represents the number of selected items
- if there is at least 1 selected item then we can go ahead and display the total in the toolbar
- textViewCounter – set this TextView to visible
- textViewTitle – set this TextView to visible
- show the total number of selected items in the textViewCounter TextView
That’s it. I hope that you have found this tutorial helpful.
Download the project's code here