7.
RecyclerViews
Written by Darryl Bayliss
In this chapter, you’ll begin to build ListMaker. An app to help organize all of your to-do lists in one handy place.
Lists are a common visual design pattern in apps, they allow developers to group collections of information together. They also allow users to scroll through and interact with each item in the list.
An item in a list can range from a line of text to more complex content like a video with comments below it — a common style used in most social media apps.
In Android development, you implement lists using a class named RecyclerView. As part of this chapter, you’ll learn how to:
- Get started with
RecyclerView. - Set up a
RecyclerViewAdapter to populate a list with data. - Set up a
ViewHolderto handle the Layout of each item in the list.
Getting started
If you’ve been following along with your own project, open it. If not, don’t worry. Locate the projects folder for this chapter and open the Listmaker app inside the starter folder. The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
With the Android Studio project open, examine the project structure. In particular, look at the following files:
- MainActivity.kt: Located in the java folder.
- activity_main.xml and content_main.xml: Located in the layout folder.
Kotlin (.kt) files drive the logic of your app. MainActivity.kt contains some familiar-looking boilerplate code related to the Activity and Menu lifecycles.
In previous chapters, you used a single Layout file to build the user interface. In this project, there are two Layout files: activity_main.xml and content_main.xml.
Why are there two?
Open activity_main.xml. With the Design view open, examine the Component Tree:
There’s a Toolbar to display menu items, as well as a FloatingActionButton. A Floating Action Button (or FAB) works similar to a button, the difference is a FAB also adheres to Googles Material Design guidelines. Don’t worry about these guidelines for now, you’ll learn more about them in Chapter 12.
Keep scanning, and you’ll see a component named include. This is where content_main.xml comes into play: The activity_main.xml Layout includes the Layout defined in content_main.xml. This is how you use both Layouts in the Activity.
While it looks strange to take this approach, it’s useful when using a Layout in multiple places within your app. It also helps when the Layout is complex enough to benefit from being split into multiple files.
Open content_main.xml and review the component tree for the layout:
It contains something called a Nav Host Fragment. We won’t touch upon these in the book, for now you’re going to replace the nav host fragment with a RecyclerView. You’ll do that in the next part.
Adding a RecyclerView
Did you notice that something important is missing from ListMaker? That’s right! It’s missing lists. At the moment, there isn’t any way to show a list, let alone the master list of lists. It’s like Inception, but…Listception instead.
Open content_main.xml in the design view. Then, select the Nav Host Fragment and delete it.
Next, go to the Palette and click Common.
Click and drag a RecyclerView from the list of components into the middle of the Layout.
Once the RecyclerView is present in the Layout, select it. Then, move to the Attributes window and change the ID to lists_recyclerview. This lets you reference the RecyclerView in your Kotlin file.
Next, in the Constraint Widget in the Layout pane, click all of the plus symbols to create constraint connections against the edges of the Layout for the RecyclerView.
Set the margins for each connection to 0.
Underneath the Constraint Widget, set layout_width and layout_height to 0dp (match_constraint).
The RecyclerView is positioned correctly. In the next part, you’ll begin to use it.
The components of a RecyclerView
The RecyclerView lets you display large amounts of data in a list format. Each piece of data is treated as an item within the RecyclerView. In turn, each of these items makes up the entire contents of the RecyclerView.
RecyclerViews have two required components it uses to display a list of items, an Adapter and ViewHolders. The following diagram shows how these components work together:
Let’s break down the flow of each component:
- The
RecyclerViewasks theAdapterhow many items it has and for an item or aViewHolderat a given position. - The
Adapterreaches into a pool of createdViewHolderit has. - Either a
ViewHolderis returned or a new one is created. - The
Adapterbinds theViewHolderto a data item at the given position. - The
ViewHolderis returned to theRecyclerViewfor display.
Adapters give the RecyclerView the data it wants to show. They have a clever way to calculate how many rows of data you want to show, which you’ll cover shortly.
ViewHolders are the visual containers for your item. Think of them as placeholders for each item in the table; this is where you tell the RecyclerView how each item should look.
As you scroll through a RecyclerView, instead of creating new ViewHolders, RecyclerView recycles ViewHolders that move offscreen and populates them with new data, ready to be shown at the bottom of the list.
This process repeats as you scroll through the RecyclerView. This recycling of ViewHolder to display list items helps to avoid janky scrolling in your app.
Note: Janky scrolling is a common term used to refer to dropped or missed frames while rendering. As an app user, you might have experienced stuttering while scrolling long lists. This is affectionately known as jank.
That concludes the whirlwind tour of RecyclerView. Now it’s time to get coding!
Hooking up a RecyclerView
Open MainActivity.kt and create a property to hold a RecyclerView, just above onCreate(savedInstanceState: Bundle?):
lateinit var listsRecyclerView: RecyclerView
You use the lateinit keyword to tell the compiler that a RecyclerView will be created sometime in the future.
Next, in the bottom of onCreate(). Link the RecyclerView in your class to the one in your layout and give it a LayoutManager and Adapter.
// 1
listsRecyclerView = findViewById(R.id.lists_recyclerview)
// 2
listsRecyclerView.layoutManager = LinearLayoutManager(this)
// 3
listsRecyclerView.adapter = ListSelectionRecyclerViewAdapter()
Here’s what you’re doing:
- Set
listsRecyclerViewby referencing the ID of theRecyclerViewyou set up incontent_main.xml. - Let the
RecyclerViewknow what kind of Layout to present your items in. This is similar to Layouts you use with your XML Layouts. You need something to arrange your items in a linear format. TheLinearLayoutManagerworks perfectly for this. You also pass in the Activity so that the Layout manager can access itsContext.
Note:
LinearLayoutManagerisn’t the only layout provided by Android. Android provides theGridLayoutManagerandStaggeredGridLayoutManager. You can read more about them over at: https://developer.android.com/guide/topics/ui/layout/recyclerview#modifying-layout
- The Adapter for the
RecyclerViewis set, letting it know to use this Adapter to acquire its data to show, and theViewHoldersto use to populate data with.
You’ll notice an error showing in Android Studio. This is because ListSelectionRecyclerViewAdapter doesn’t exist. You’ll create this in the next part.
Setting up a RecyclerView Adapter
Right-click com.raywenderlich.listmaker in the Project navigator. In the floating options that appear, hover over New. In the next set of options that appear, click Kotlin File/Class.
In the popup that appears, enter ListSelectionRecyclerViewAdapter for the Name and change the Kind drop-down to Class. Then, press the enter key.
Android Studio creates the class for you. Repeat the process and create a ViewHolder class too. Name this new class ListSelectionViewHolder.
You’re ready to turn these classes into recycling machines. Open ListSelectionViewHolder.kt, then add a primary constructor to the class, so you can pass in the View for the ViewHolder and have it extend RecyclerView.ViewHolder:
class ListSelectionViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
}
Open ListSelectionRecyclerViewAdapter.kt and extend the class to inherit from RecyclerView.Adapter<ListSelectionViewHolder>():
class ListSelectionRecyclerViewAdapter :
RecyclerView.Adapter<ListSelectionViewHolder>() {
}
Here in the subclass, you pass in the type of ViewHolder you want the RecyclerView Adapter to use. This makes the RecyclerView aware of the type of ViewHolder it expects to use so you can reference it in a few methods you’ll implement shortly.
Notice that the name of the class is underlined with red. Move your mouse cursor over it, and Android Studio informs you why there’s an error.
Because this class inherits from RecyclerView.Adapter, it needs to implement additional methods so it knows what to do when used in conjunction with a RecyclerView.
With your cursor over the class name, press Option-Enter to get a selection of options.
Note: This keystroke assumes you’re using a Mac for Android development; however, Windows and Linux versions of Android Studio provide an equivalent shortcut through Alt-Enter.
Having trouble getting this to work? Alternatively, hover the cursor over the class name and press Control-I or select Code along the top toolbar of Android Studio, and click Implement Members
Click Implement Members, and a new window appears with options for various methods to implement. Since the Recycler Adapter needs each one, you’ll add them all.
Ensure onCreateViewHolder() is highlighted, then Shift-click on the bottom-most available member.
Finally, click OK and Android Studio does the rest of the work for you, by generating the methods needed for a RecyclerView Adapter.
Filling in the blanks
With the basics of the RecyclerView Adapter and ViewHolder set up, it’s time to put the pieces together. First, you need content for the RecyclerView to show. For now, you’ll add some mock titles to show off the RecyclerView.
You also need a Layout for the ViewHolder so the RecyclerView knows how each item within it should look. Finally, you need to bind the titles to the ViewHolder at the right time depending on what position it has within the RecyclerView.
You’ll implement the mock list titles first. In ListSelectionRecyclerViewAdapter.kt, add the following new variable at the top of the class:
val listTitles = arrayOf("Shopping List", "Chores", "Android Tutorials")
Here, you create an array of strings to use as the list titles. In future chapters, you’ll change this to something more sophisticated — but for now, an array will do.
getItemCount() determines how many items the RecyclerView has. You want the size of the array to match the size of the RecyclerView, so you return that.
In getItemCount(), you return the size of the array, like so:
override fun getItemCount(): Int {
return listTitles.size
}
Your Adapter now knows how many items to display on the screen. Next, you need to create the Layout needed for the ViewHolder to display each item in the RecyclerView.
Creating the ViewHolder
In the Project navigator on the left, right-click on the layout folder and create a new Layout resource file:
In the new window that appears, type list_selection_view_holder for the File name; this serves as the name of the Layout when created.
The Root Element defines the first tag in the Layout. For this Layout, you’ll use a LinearLayout, so type LinearLayout into the text field.
Click OK at the bottom of the window. Android Studio opens your new Layout, ready for you to add the Views you want the ViewHolder to contain. You need two TextViews here: one to tell you the position of the list in the RecyclerView, and one to tell you the name of the list.
With the Design window open, drag a TextView on to the Layout.
In the Attributes window to the right of Android Studio, change the ID of the TextView to itemNumber. Also, change the layout_width and layout_height attributes to wrap_content, and remove the placeholder text from the text attribute:
To ensure the text isn’t sitting too closely to the edge of the screen, you need to give it space on its left edge. You do this by setting the layout_margin parameter, inside the Layout_Margin attribute to add padding to this TextView.
First, find the layout_margin attribute and click the arrow next to it to reveal a drop-down for each of the parameters.
In the layout_margin text field, type 16dp. This tells the TextView to pad itself by 16 density pixels (dp) on all sides.
Note: A density pixel is a unit of measurement Android uses to lay out your View relative to the size of the device screen. Because devices have many different screen sizes, using absolute pixels isn’t feasible as screens will render differently from device to device. To learn more, review the Android Developer Documentation: https://developer.android.com/guide/practices/screens_support.html.
With that done, repeat the process for the first TextView, by dragging another TextView into the Layout. Place it underneath the first TextView.
Change the ID of the second TextView to itemString. Change the layout_width and layout_height to wrap_content. Remove the placeholder text from the text attribute and change the layout_margin parameter in the layout_margin attribute to 16dp.
You’re nearly done with this Layout — there’s only one more thing to do.
Currently, the TextViews are laid out in a vertical orientation. However, a horizontal orientation is better suited for this app, so you need to change some attributes on the LinearLayout the Layout uses.
Click the LinearLayout in the Component Tree window:
In the Attributes Window, click the drop-down button on the orientation attribute and select horizontal.
Change the layout_width and layout_height attributes to wrap_content to make the ViewHolder only as big as it needs to be:
You’re ready to use the Layout. Open ListSelectionRecyclerViewAdapter.kt and change onCreateViewHolder() to use the view holder layout:
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): ListSelectionViewHolder {
// 1
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.list_selection_view_holder,
parent,
false)
// 2
return ListSelectionViewHolder(view)
}
The method does two things:
- First, it uses a LayoutInflater object to create a layout programmatically. It uses the parent context of the Adapter to create itself and attempts to inflate the Layout you want by passing in the layout name and the parent ViewGroup so the View has a parent it can refer to. The Boolean value is used to specify whether the View should be attached to the parent. Always use false for
RecyclerViewlayouts as theRecyclerViewattaches and detaches the Views for you.
> **Note**: `LayoutInflater` is a system utility used to instantiate (or "inflate") a layout XML file into its corresponding View objects.
- A
ListSelectionViewHolderobject is created, passing in the view created from the layout. Finally, the ViewHolder is returned from the method.
Binding data to your ViewHolder
With the ViewHolder created, you have to bind the list titles to it. To do this, you need to know what Views to bind your data to. You already created the TextFields in your ViewHolder Layout, but you haven’t yet referenced these in code yet.
Open ListSelectionViewHolder.kt and add the following properties to the class, so the ViewHolder has references to the new TextViews:
val listPosition = itemView.findViewById(R.id.itemNumber) as TextView
val listTitle = itemView.findViewById(R.id.itemString) as TextView
Next, open ListSelectionRecyclerViewAdapter.kt again and edit onBindViewHolder() so it sets a value for each of the TextViews on the ViewHolder:
override fun onBindViewHolder(holder: ListSelectionViewHolder, position: Int) {
holder.listPosition.text = (position + 1).toString()
holder.listTitle.text = listTitles[position]
}
For each call of onBindViewHolder(), you take the TextViews you created in the ViewHolder and populate them with their position in the list and the name of the list from the listTitles array.
This is called repeatedly as you scroll through the RecyclerView.
The moment of truth
Finally! You can see the fruits of your labors. Click the Run App button at the top of Android Studio and see what happens.
Fantastic, you now have a list of titles and the position they hold in the RecyclerView. Great job!
Where to go from here?
There are many moving pieces required to use RecyclerView to display a list of data. However, don’t be afraid to use them, they’re an essential construct for creating Android apps that provide fluid and intuitive user experiences. They are as common as Buttons and TextViews.
If you want to learn more about RecyclerView, review the documentation on the developer website https://developer.android.com/guide/topics/ui/layout/recyclerview.html. It dives deeper into the inner workings of RecyclerView and describes how to animate changes to list items.
If you’re still looking for more, check out the tutorial on the Ray Wenderlich site https://www.raywenderlich.com/170075/android-recyclerview-tutorial-kotlin which shows how to use different LayoutManagers, and how to swipe to delete items in your list.
Finally, if you find using findViewById cumbersome, you can leverage Kotlin to find your Views for you using the Kotlin Android Extensions (KAE) library. This library binds your Views to your code automatically, and provides many more benefits. You can learn how to use KAE over at: https://www.raywenderlich.com/84-kotlin-android-extensions.