This tutorial describes how to use the RecyclerView widget in Android applications.

1. Android and list or grids

1.1. Using lists or grids in Android

The display of elements in a list or grids is a very common pattern in mobile applications. The user sees a collection of items and can scroll through them. The collection of items can be a list, a grid or another structured representations of data. Such an activity is depicted in the following picture.

Listview example

The user interacts with the collection of items via touch events or the toolbar. Individual items can be selected. This selection may update the toolbar or may trigger a detailed screen based on the selection. The following graphic sketches that.

Listview example

1.2. Using RecyclerView

The RecyclerView class supports the display of a collection of data.

It is a modernized version of the ListView and the GridView classes provided by the Android framework. Recycler view addresses several issues that the existing widgets have. It enforced a programming style that results in good performance. It also comes with default animations for removing and adding elements.

RecyclerView allow to use different layout managers for positioning items.

Recycler view uses a ViewHolder to store references to the views for one entry in the recycler view. A ViewHolder class is a static inner class in your adapter which holds references to the relevant views. With these references your code can avoid the time-consuming findViewById() method to update the widgets with new data.

1.3. Adapters

An adapter manages the data model and adapts it to the individual entries in the widget. It extends the RecyclerView.Adapter class and is assigned to the recycler view via the RecyclerView.setAdapter method. The input to the adapter of an recycler view can be any arbitrary Java objects. Based on this input the adapter must return the total number of items via its getItemCount() method.

The adapter prepares the layout of the items by inflating the correct layout for the individual data elements. This work is done in the onCreateViewHolder method. It returns an object of type ViewHolder per visual entry in the recycler view.

This instance is used to access the views in the inflated layout. The onCreateViewHolder method is only called then a new view must be created.

Every visible entry in a recycler view is filled with the correct data model item by the adapter. Once a data item becomes visible, the adapter assigns this data to the individual widgets which he inflated earlier. This work in done in the onBindViewHolder method.

For example, an entry in a list may have an image on the left side and two text lines in the middle as depicted in the following graphic.

Recycler view example layout

A layout file for a such a line might look like the following.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="6dip"
        android:contentDescription="TODO"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/secondLine"
        android:layout_width="fill_parent"
        android:layout_height="26dip"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@id/icon"
        android:ellipsize="marquee"
        android:maxLines="1"
        android:text="Description"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/firstLine"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/secondLine"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_alignWithParentIfMissing="true"
        android:layout_toRightOf="@id/icon"
        android:gravity="center_vertical"
        android:text="Example application"
        android:textSize="16sp" />

</RelativeLayout>

1.4. Gradle dependency to use recycler view

The RecyclerView widget is delivered as library and can be used as of API 7 level or higher. Add the a dependency to the latest version of this library to your Gradle build file to use it.

dependencies {
    ...
    implementation "com.android.support:recyclerview-v7:25.3.1"
}

1.5. Default layout manager

The layout manager decides how the data in the RecyclerView is displayed. The recycler view library provides the following build-in layout managers.

  • LinearLayoutManager shows items in a vertical or horizontal scrolling list.

  • GridLayoutManager shows items in a grid.

  • StaggeredGridLayoutManager shows items in a staggered grid.

1.6. Relevant implementation classes to use RecyclerView

The implementation of RecyclerView requires a few classes to be implemented. The most important classes are listed in the following table

Table 1. Important classes of the RecyclerView API
Class Purpose Optional

Adapter

Provides the data and responsible for creating the views for the individual entry

Required

ViewHolder

Contains references for all views that are filled by the data of the entry

Required

LayoutManager

Contains references for all views that are filled by the data of the entry

Required, but default implementations available

ItemDecoration

Responsible for drawing decorations around or on top of the view container of an entry

Default behavior, but can be overridden

ItemAnimator

Responsible to define the animation if entries are added, removed or reordered

Default behavior, but can be overridden

You can also provide your custom implementations for the layout managers and animations.

1.7. Handling click events in recycler view

Touch events, like clicking should be handled by the views in the recycler view. If the view should trigger something in the object in which it is used (activity or fragment), you can pass via the constructor of the adapter to it. This allows the adapter to store a reference to the object and call its methods for feedback.

1.8. Layouts in recycler view

The adapter needs to provide the view hierarchy for each entry. Typical this is done by inflating an XML layout file.

Use wrap_content or ?android:attr/listPreferredItemHeight for the height of the view container for the row.

<LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/listPreferredItemHeight">
        <!-- views contained in each line -->
</LinearLayout>

This root of the layout is typically a ViewGroup (layout manager) and contains several other views. The following graphic shows a list with different layouts for odd and even rows.

Adapter provides data and defines the layout per row

Within the getItemViewType method the recycler view determines which type should be used for data. The framework calls automatically the onCreateViewHolder method if needed for this type. In this method you inflate the correct layout for the type and return a fitting view holder.

1.9. Custom animations

To customize the animations in the RecyclerView, implement your own animation by extending the RecyclerView.ItemAnimator class and use the RecyclerView.setItemAnimator() method to assign it to your widget.

1.10. Filtering and sorting

Filtering and sorting of the data is handled by the adapter. You need to implement the logic in your custom adapter implementation.

1.11. Data updates in the adapter

The notifyItemInserted(position) method on the adapter can be used to notify the view that a new entry has been inserted at a certain position.

The notifyItemRemoved(position); method can be used to notify the view that a entry has been deleted in a certain position.

1.12. Swipe support for RecyclerView

The ItemTouchHelper class that makes swipe-to-dismiss and drag-and-drop simple to implement. Implement the onMove method for drag and drop and the onSwiped for swip support.

2. Exercise: Using RecyclerView in a new Android application

In this exercise you create a project which uses the RecyclerView class to display a list.

RecyclerView example

2.1. Create project and add the Gradle dependency

Create a new Android project using the com.vogella.android.recyclerview top level package name.

Add the following dependency to your Gradle build file.

dependencies {
    ...
    implementation "com.android.support:recyclerview-v7:25.3.1"
}

2.2. Create layout files

Create or update the layout file called activity_main.xml so that it contains the RecyclerView

<?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="match_parent" >

    <!-- A RecyclerView with some commonly used attributes -->

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="12dp"
        android:layout_marginRight="12dp"
        android:elevation="2dp"
        android:src="@android:drawable/ic_menu_add" />

</RelativeLayout>

Note: The ImageView has the android:elevation attribute set, this instructs Android to draw a shadow for it.

Also create a layout called row_layout to use in each item.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="6dip"
        android:contentDescription="TODO"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/secondLine"
        android:layout_width="fill_parent"
        android:layout_height="26dip"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@id/icon"
        android:ellipsize="marquee"
        android:maxLines="1"
        android:text="Description"
        android:textSize="12sp" />

    <TextView
        android:id="@+id/firstLine"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/secondLine"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_alignWithParentIfMissing="true"
        android:layout_toRightOf="@id/icon"
        android:gravity="center_vertical"
        android:text="Example application"
        android:textSize="16sp" />

</RelativeLayout>

Create the following adapter class.

package com.vogella.android.recyclerview;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private List<String> values;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView txtHeader;
        public TextView txtFooter;
        public View layout;

        public ViewHolder(View v) {
            super(v);
            layout = v;
            txtHeader = (TextView) v.findViewById(R.id.firstLine);
            txtFooter = (TextView) v.findViewById(R.id.secondLine);
        }
    }

    public void add(int position, String item) {
        values.add(position, item);
        notifyItemInserted(position);
    }

    public void remove(int position) {
        values.remove(position);
        notifyItemRemoved(position);
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(List<String> myDataset) {
        values = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        LayoutInflater inflater = LayoutInflater.from(
                parent.getContext());
        View v =
                inflater.inflate(R.layout.row_layout, parent, false);
        // set the view's size, margins, paddings and layout parameters
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        final String name = values.get(position);
        holder.txtHeader.setText(name);
        holder.txtHeader.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                remove(position);
            }
        });

        holder.txtFooter.setText("Footer: " + name);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return values.size();
    }

}

Now you can configure the RecyclerView in your activity.

package com.vogella.android.recyclerview;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {
    private RecyclerView recyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager layoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        // use this setting to
        // improve performance if you know that changes
        // in content do not change the layout size
        // of the RecyclerView
        recyclerView.setHasFixedSize(true);
        // use a linear layout manager
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        List<String> input = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            input.add("Test" + i);
        }// define an adapter
        mAdapter = new MyAdapter(input);
        recyclerView.setAdapter(mAdapter);
    }
}

3. Exercise Add swipe to dismiss support to your Recyclerview

We want to implement swipe to dismiss so that the user can swipe elements out.

3.1. Implement the following coding after your recyclerview declaration.

The following example code adds swipe to dismiss to your recyclerview.

// put this after your definition of your recyclerview
// input in your data mode in this example a java.util.List, adjust if necessary
// adapter is your adapter
 ItemTouchHelper.SimpleCallback simpleItemTouchCallback =
                new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
                    @Override
                    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder
                            target) {
                        return false;
                    }
                    @Override
                    public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
                        input.remove(viewHolder.getAdapterPosition());
                        adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
                    }
                };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

3.2. Validate

Start your application and ensure that you can remove items in your list with a swipe gesture.

4. Optional exercise: Use Swipe to refresh in your list fragment

Add Swipe to refresh to your list fragment.

4.1. Implement swipe to refresh

You found the following code in the Internet. Try to adjust it to add it your recycler view implementation.

This code is just example code. You need to adjust this code to fit to your example.

dependencies {
    implementation 'com.android.support:support-v4:25.4.0'
}
<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swiperefresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

</android.support.v4.widget.SwipeRefreshLayout>
Even if you provide the swipe-to-refresh design pattern, you should still add a refresh button to your overflow menu in your toolbar to support the refresh for users unable to swipe.

In the SwipeRefreshLayout.OnRefreshListener method you implement the logic to update your view.

mySwipeRefreshLayout.setOnRefreshListener(
    new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            doYourUpdate();
        }
    }
);


private void doYourUpdate() {
   // TODO implement a refresh
   setRefreshing(false); // Disables the refresh icon
}

4.2. Validate

Start your application and pull down your list. The refresh indicator should be shown and should vanish after the update happens. You may want to add a test delay to the data refresh to ensure you can see the update.

5. Recycler view resources