How to Create Lists using RecyclerView

In this post, we’re going to look at the newer RecyclerView to build more complicated lists and list items. RecyclerView was a recently added UI item that gave developers fine-grain control and power over their lists or grids. However, as we’ll find out, that great power comes with great responsibility. RecyclerView doesn’t provide much of the basic functionality that ListView does, so we’ll have to implement it ourselves. In this post, we’ll be implementing a layout with an icon on the left and descriptive text on the right.

Note: Before we create the project, we’ll need to make sure we have the support library downloaded. The support library was released to allow for greater backwards-compatibility among the range of physical devices and their different Android versions in the ecosystem. Instead of waiting for a software update to the latest version of Android, a backwards-compatible version would be released in the support library that developers can use right away. Open up the SDK manager and click on the SDK Tools tab. Make sure Android Support Library and Android Support Repository are checked. If not, check them, wait for the packages to download, and restart Android Studio. Now we can begin!

All of the source code for this project can be found here.

Creating the Project

Let’s open up Android Studio and create a new project called RecyclerViewDemo (give it any package name you want!) The minimum SDK version should be Android 6.0 Marshmallow and we can created an empty activity with the default values. Before we can use RecyclerView, we need to add that component to our build files since it’s in the support library. In the Project view on the left, open up the Gradle Scripts node and the build.gradle (Module: app) file. Note that there is a build.gradle (Project: RecyclerViewDemo), but that serves a different purpose! Towards the bottom, there should be a section labeled dependencies. This tells Android what our app needs to function properly. Between the curly braces, add the following line of code: compile ‘com.android.support:recyclerview-v7:23.1.+’ .

This line will tell Android to include RecyclerView in our project. The first number MUST correspond to the compileSdkVersion number we see at the top of the file. When you type that line, it might be highlighted in yellow and we can see the suggestion if we hover our cursor over it. This warning can generally be discarded since it just provides us with the warning that we should be using the absolute latest version of RecyclerView. We can change the plus (+) symbol to the same version as the dependency on the appcompat-v7 library in the same dependencies block. After we make changes to this file, towards the top of the main coding area, we should have an option to “Sync Now” and gradle will recompile our project with the modified dependencies.

Now we can start building the view, which turns out to be as simple as ListView. Open up the activity_main.xml layout file in res/layout and replace the contents with the following:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:scrollbars="vertical"
    android:id="@+id/recyclerView"
    tools:context=".MainActivity" />

This looks strikingly similar to the ListView, however, we have to specify the scrollbars property since RecyclerView is flexible to be used either horizontally or vertically. This is all we’ll need for our main view.

We need to create a layout for each of our list items, but, before we do that, we need to define a border for each of our list items. ListView did this for us, but we need to do this as well. Right-click on the res/drawable folder and go to New → Drawable Resource File. We’ll name it border and replace its contents with the following XML code to define a border and background.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFFFF" />
    <stroke
        android:width="1dp"
        android:color="#CCCCCC" />
</shape>

Now we need to create a layout for each list item in our RecyclerView. To do this, we can right-click on the res/layout folder and go to New → Layout Resource File. We can call it list_item and make the Root Element a RelativeLayout. Afterwards, we’ll click OK and we should have a new file generated for us in the the design view. In this view, we can simply drag-and-drop UI items from the palette on the left and change any properties in the properties pane on the bottom left. This will generate the underlying XML code for us. We’ll need an ImageView for the icon and a TextView for the description. Click on the Text tab at the bottom of the main pane and replaced the contents with the following.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeightLarge"
    android:background="@drawable/border">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentStart="true"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginEnd="@dimen/activity_horizontal_margin"
        android:contentDescription="Icon" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_toEndOf="@id/imageView"
        android:gravity="center_vertical"
        android:textSize="16sp"/>

</RelativeLayout>

The above code will properly place the ImageView and TextView within the RelativeLayout and set the background of the RelativeLayout to the border we defined. Now we can work on the model. We’ll need some object to represent an image and a text description. We can create a class to encapsulate this data. Find the MainActivity.java file, and, in the package above it (NOT ending in (androidTest)), go to New → Java Class and we’ll call it IconData. We’ll need two simple fields: a String text and an int resource id. Put the following in the IconData class.

public class IconData {
    private String description;
    private int imgId;

    public IconData(String description, int imgId) {
        this.description = description;
        this.imgId = imgId;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getImgId() {
        return imgId;
    }

    public void setImgId(int imgId) {
        this.imgId = imgId;
    }
}

Suppose we have member variables in a class and we want getters and setters for these fields. We can actually have Android Studio generate them AND parameterized constructors as well! On the top menu bar, we can go to Code → Generate, then select Getters and Setters, and select both fields (using Shift key) and click OK. We can also generate parameterized constructors in the same way, except choosing Constructor in the popup.

Now that we have a container for our data, we can create the custom adapter that will show our data. Create a new class in the same package and call it IconAdapter. Inside of it, we’ll create a public static inner class ViewHolder whose superclass is RecyclerView.ViewHolder. We’ll get an error, but we can fix it by clicking our cursor somewhere in the error and press Alt+Enter to generate a constructor matching super. Since we’re using a more complicated layout, we’ll need to extract and keep references to views by adding them as member variables and calling findViewById(int) on the view passed into the constructor.

public static class ViewHolder extends RecyclerView.ViewHolder {
    public ImageView imageView;
    public TextView textView;
    public ViewHolder(View itemView) {
        super(itemView);
        this.imageView = (ImageView) itemView.findViewById(R.id.imageView);
        this.textView = (TextView) itemView.findViewById(R.id.textView);
    }
}

The ViewHolder pattern is a way to make lists very efficient by recycling the list items that go off the screen. When we have a list, we create enough memory and resources for list items on the screen plus a few more for smooth scrolling. When list items, go off the top of the screen, the idea is to recycle those views by replacing their contents with that of a new data item and putting them at the bottom. This allows us to use the least amount of memory while still giving the user a smooth experience. Now that we’ve instantiated our ViewHolder, we’ll need to make our IconAdapter actually extend RecyclerView.Adapter with the generic being our ViewHolder. We’ll have to implement the abstract methods soon, but, currently, our entire IconAdapter class should look like the following:

public class IconAdapter extends RecyclerView.Adapter<IconAdapter.ViewHolder> {

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView imageView;
        public TextView textView;
        public ViewHolder(View itemView) {
            super(itemView);
            this.imageView = (ImageView) itemView.findViewById(R.id.imageView);
            this.textView = (TextView) itemView.findViewById(R.id.textView);
        }
    }

}

Now we need to start implementing the adapter and we can take full use of the ViewHolder pattern to make this task easy. We first need some way to store data, and we’ll use an array of IconData for that purpose. After creating a member variable and generating a parameterized constructor, we get the following:

public class IconAdapter extends RecyclerView.Adapter<IconAdapter.ViewHolder> {
    private IconData[] data;

    public IconAdapter(IconData[] data) {
        this.data = data;
    }
    ...

Now we can start implementing the primary methods. getItemCount()  can just return the length of our data array:

    ...
    @Override
    public int getItemCount() {
        return data.length;
    }
    ...

onCreateViewHolder(ViewGroup, int)  will need to “inflate” our custom list item view from our xml layout. However, the LayoutInflater needs a Context object and we can’t provide one from just our adapter, but we can get one from the ViewGroup object. Then we can inflate the view and get a View object. As the name suggests, we’ll also need to create a new ViewHolder object that will be associated with that row and we can return that object:

    ...
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View listItem= layoutInflater.inflate(R.layout.list_item, parent, false);
        ViewHolder viewHolder = new ViewHolder(listItem);
        return viewHolder;
    }
    ...

onBindViewHolder(ViewHolder, int)  is where we actually populate the views in the list item row with data. Because of the ViewHolder pattern, this is as simple as getting our ImageView and TextView from our ViewHolder and setting their image or text. Within the parentheses of setText(String)  and setImageResource(int) , we grab the single IconData object at that particular position and extract its description or image ID:

    ...
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.textView.setText(data[position].getDescription());
        holder.imageView.setImageResource(data[position].getImgId());
    }
    ...

Now our adapter is complete and we can begin using it! Let’s go to MainActivity.java and look in the onCreate(Bundle)  method. Inside, we should just have a class to the super class and a method call to setContentView(int)  that will associate this Activity with the given layout. We need to put any data that accesses views after the call to setContentView(int) since, before then, there is no inflated view! Let’s add some code after that call so our method looks like the following:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IconData[] data = new IconData[] {
                new IconData("Delete", android.R.drawable.ic_delete),
                new IconData("Alert", android.R.drawable.ic_dialog_alert)
        };

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        IconAdapter adapter = new IconAdapter(data);

        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

    }

First, we create an array of static IconData that will be displayed in the RecyclerView. You might notice on the left margin Android Studio will show a small thumbnail icon of each of the icons that we use for each IconData object. Then, we grab a reference to the RecyclerView and create a new IconAdapter using that array of IconData. Now we can set properties on the RecyclerView. The first is there for performance: if the RecyclerView isn’t going to change in size, then Android can make it even smoother! The next is the layout of the RecyclerView. It can present a list (LinearLayoutManager), a grid (GridLayoutManager), or a staggered grid (StaggeredGridLayoutManager). We’ll be presenting a list so we’ll use a LinearLayoutManager. Finally, we set the adapter of our RecyclerView to be our IconAdapter. Running our application, we should see the following.

RecyclerView – 1

Feel free to use any text description or icons (many are in android.R.drawable class and start with “ic_”!). Add as many or as few items you want! This will give you a chance to see what you can do with a RecyclerView.

Conclusion

In this post, we learned about the more power and flexible RecyclerView. However, with this power, we had to do more work ourselves to get the desired result. For instance, we had to create a custom list item layout and corresponding ViewHolder. Then we had to create an adapter that used that ViewHolder. then we can finally use that adapter in our RecyclerView. When debating on using RecyclerView or a plain ListView, generally choose the RecyclerView since it’s intended to replace ListView. On the other hand, if you’re presenting a small, simple list, a ListView might fit your needs to get that data presented quickly.