4.4: Create a Recycler View

Contents:

Displaying and manipulating a scrollable list of similar data items, as you did in the scrolling view practical, is a common feature of apps. For example, contacts, playlists, photos, dictionaries, shopping lists, an index of documents, or a listing of saved games are all examples of scrollable lists.

Earlier in this class, you used ScrollView to perform scrolling of other Views. ScrollView is easy to use, but it is not recommended for production use, especially for long lists of scrollable items.

RecyclerView is a subclass of ViewGroup and is a more resource-efficient way to display scrollable lists. Instead of creating a view for each item, whether or not it's visible, RecyclerView creates a limited number of list items and reuses them for visible content.

In this series of practicals you will use a RecyclerView to:

  • Display a scrollable list of items.
  • Add a click handler to each item.
  • Add items to the list using a floating action button (FAB), the pink button in the screenshot below. A floating action buttons can be used for common actions, or a promoted action, that is, an action that you want the user to take.

What you should already KNOW

For this practical you should be familiar with how to:

  • Create a Hello World app with Android Studio.
  • Implement different layouts for apps.
  • Create and using string resources.
  • Add an onClick handler to a view.

What you will LEARN

In this practical, you will learn to:

  • Use the RecyclerView class to display items in a scrollable list.
  • Dynamically add items to the RecyclerView as they become visible through scrolling.
  • Perform an action when the user taps a specific item.
  • Show a floating action button and perform an action when the user taps it.

What you will DO

Create a new application that uses a RecyclerView to display a list of items as a scrollable list and associate click behavior with the list items. Use a floating action button to let the user add items to the RecyclerView.

App Overview

The "RecyclerView" app will display a long list of words. Preview of the RecyclerView Sample App

  • Tapping an item marks it clicked.
  • Tapping the floating action button adds an item.
  • There is no user input of words for this app.

Task 1. Create and configure a new project

In this task, you will create and configure a new project for the RecyclerView sample app.

1.1. Create the project

  1. Start Android Studio and create a new project with the following parameters:
    Attribute Value
    Application Name RecyclerView
    Company Name com.example.android or your own domain
    Phone and Tablet Minimum SDK API15: Android 4.0.3 IceCreamSandwich
    Template Empty Activity
    Generate Layout file box Checked
  2. Run your app on an emulator or hardware device. You should see the "RecyclerView" title and "Hello World" in a blank view.

1.2. Add support libraries to the build file

In order to use the RecyclerView and the floating action button (FAB), you need to add the respective Android Support Libraries to your build.

Why: Android Support libraries provide backward-compatible versions of Android framework APIs, additional UI components and a set of useful utilities. The RecyclerView class is located in the Android Support package; two dependencies must be included in the Gradle build process to use it.

Follow these steps and refer to the screenshot:

  1. In Android Studio, in your new project, make sure you are in the Project pane (1) and in the Android view (2).
  2. In the hierarchy of files, find the Gradle Scripts folder (3).
  3. Expand Gradle Scripts, if necessary, and open the build.gradle (Module: app) file (4). build.gradle file in Android Studio
  4. Towards the end of the build.gradle (Module: app) file, find the dependencies section.
  5. Add these two dependencies as the last two lines inside the dependencies section:
    compile 'com.android.support:recyclerview-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
    
    • There is probably an existing line like this one, but the number may be different: compile 'com.android.support:appcompat-v7:23.1.1'
    • Add your lines below that line.
    • Match the version number of your lines to the version number of that existing line.
    • Make sure the version numbers of all the libraries are the same and match up with the compiledSdkVersion at the top of the file. If these don't match, you will get a build time error.
  6. If prompted, sync your app now.
  7. Run your app. You should see the same "RecyclerView" app displaying "Hello World". If you get gradle errors, sync your project. You do not need to install additional plugins.

Solution:

This is an example of the dependencies section of the build.gradle file. Your file may be slightly different and your entries may have a different version number.

dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   testCompile 'junit:junit:4.12'
   compile 'com.android.support:appcompat-v7:23.1.1'
   compile 'com.android.support:recyclerview-v7:23.1.1'
   compile 'com.android.support:design:23.1.1'
}

Task 2. Create a dataset

Before you can display anything, you need data to display. In a more sophisticated app, your data could come from internal storage (a file, SQLite database, saved preferences), from another app (Contacts, Photos), or from the internet (cloud storage, Google Sheets, or any data source with an API). For this exercise, you will simulate data by creating it in the main activities onCreate() method.

Why: Storing and retrieving data is a topic of its own covered in the data storage chapter. You will have an opportunity to extend your app to use real data in that later lesson.

2.1. Add code to create data

In this task you will dynamically create a linked list of twenty word strings that end in increasing numbers, such that ["Word 1", "Word 2", "Word 3", …. ].

You must use a LinkedList for this practical. Refer to the solution code, if you need help.

  1. Open the MainActivity.java file.
  2. Add a private member variable for the mWordList linked list.
  3. Add an integer counter mCount variable to track the word's number.
  4. Add code that populates mWordList with words. Concatenate the string "Word" with the value of mCount, then increase the count.
  5. Since you cannot display the words yet for testing, add a log statement that verifies that words are correctly added to the linked list.
  6. Run your app to make sure there are no errors.

The app UI has not changed, but you should see a list of log messages in logcat, such as: android.example.com.wordlist D/WordList: Word 1

Solution:

Class variables:

private final LinkedList<String> mWordList = new LinkedList<>();
private int mCount = 0;

In the onCreate method of MainActivity:

for (int i = 0; i < 20; i++) {
   mWordList.addLast("Word " + mCount++);
   Log.d("WordList", mWordList.getLast());
}

Task 3. Create a RecyclerView

In this practical, you will display data in a RecyclerView. Since there are several parts to creating a working RecyclerView, make sure you immediately fix any errors that you see in Android Studio.

To display your data in a RecyclerView, you need the following parts:

  • Data. You will use the mWordList.
  • A RecyclerView. The scrolling list that contains the list items.
  • Layout for one item of data. All list items look the same.
  • A layout manager. The layout manager handles the organization (layout) of user interface components in a view. You have already used the LinearLayout in a previous practical where the Android system handles the layout for you. RecyclerView requires an explicit layout manager to manage the arrangement of list items contained within it. This layout could be vertical, horizontal, or a grid. You will use a vertical linear layout manager provided by Android.
  • An adapter. The adapter connects your data to the RecyclerView. It prepares the data in a view holder. You will create an adapter that inserts into and updates your generated words in your views.
  • A view holder. Inside your adapter, you will create a ViewHolder class that contains the view information for displaying one item from the item's layout.

The diagram below shows the relationship between the data, the adapter, the view holder, and the layout manager. RecyclerView architecture

Implementation steps overview

To implement these pieces, you will need to:

  1. Create the XML layout for the "RecyclerView" app (activity_main.xml).
  2. Create the XML layout used to lay out one list item, which is WordListItem (wordlist_item.xml).
  3. Create an adapter (WordListAdapter) with a view holder (WordViewHolder). Implement the method that takes the data, places it in a view holder, and let's the layout manager know to display it.
  4. In the onCreate method of MainActivity, create a RecyclerView and initialize it with the adapter and a standard layout manager. Let's do these one at a time.

3.1. Create the main layout in activity_main.xml

In the previous apps, you used LinearLayout to arrange your views. In order to accommodate the RecyclerView and the floating action button that you will add later, you need to use a different view group called a coordinator layout. CoordinatorLayout is more flexible than LinearLayout when arranging views. For example, views like the floating action button can overlay other views.

In main_activity.xml, replace the code created by the Empty Activity with code for a CoordinatorLayout, and then add a RecyclerView:

  1. Open build.gradle (Module:app) and verify that the recycler view dependency exists.
    compile 'com.android.support:recyclerview-v7:24.1.1'
    
  2. Open activity_main.xml.
  3. Select all the code in activity_main.xml and replace it with this code:
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    </android.support.design.widget.CoordinatorLayout>
    
  4. Inspect the code and note the following:
    • The properties specified for this view group are the same as for LinearLayout, because some basic properties, such as layout_width and layout_height, are required for all views and view groups.
    • Because CoordinatorLayout is in the support library, you have to specify the full path to the support library. You will have to do the same for the RecyclerView.
  5. Add the RecyclerView code inside the CoordinatorLayout:
    • You need to specify the full path, because RecyclerView is part of the support library.
      <android.support.v7.widget.RecyclerView>
      </android.support.v7.widget.RecyclerView>
      
  6. Give your RecyclerView the following properties:
    Attribute Value
    android:id "@+id/recyclerview"
    android:layout_width match_parent
    android:layout_height match_parent
  7. Run your app, and make sure there are no errors displayed in logcat. You will only see a blank screen, because you haven't put any items into the RecyclerView yet.

Solution:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

</android.support.design.widget.CoordinatorLayout>

3.2. Create the layout for one list item

The adapter needs the layout for one item in the list. All the items use the same layout. You need to specify that list item layout in a separate layout resource file, because it is used by the adapter, separately from the RecyclerView.

Create a simple word item layout using a vertical LinearLayout with a TextView:

  1. Right-click the app/res/layout folder and choose New > Layout resource file.
  2. Name the file wordlist_item and click OK.
  3. In Text mode, change the LinearLayout that was created with the file to match with the following attributes. Extract resources as you go.
    Attribute Value
    android:layout_width "match_parent"
    android:layout_height "wrap_content"
    android:orientation "vertical"
    android:padding "6dp"
  4. Add a TextView for the word to the LinearLayout:
    Attribute Value
    android:id "@+id/word"
    android:layout_width "match_parent"
    android:layout_height "wrap_content"
    android:textSize "24sp"
    android:textStyle "bold"

3.3 Create a style from the TextView attributes

You can use styles to allow elements to share groups of display attributes. An easy way to create a style is to extract the style of a UI element that you already created. Extract the style information for the word text view:

  1. While you have wordlist_item.xml open, hover the mouse over the TextView section you just created and Right-click > Refactor > Extract > Style.
  2. In the Extract Android Style dialog,
    • Name your style word_title.
    • Leave all boxes checked.
    • Check the Launch 'Use Style Where Possible' box.
    • Click OK.
  3. When prompted, apply the style to the Whole Project.
  4. Find and examine the word_title style in values/styles.xml.
  5. Go back to wordlist_item.xml. The text view now references the style instead of using individual styling properties.
  6. Run your app. Since you have removed the default "Hello World" text view, you should see the "RecyclerView" title and a blank view.

Solution:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="6dp">

    <TextView
        android:id="@+id/word"
        style="@style/word_title" />

</LinearLayout>

3.4. Create an adapter with a view holder

Android uses adapters (from the Adapter class) to connect data with their views. There are many different kinds of adapters available. You can also write your own custom adapters. In this task you will create an adapter that associates your list of words with word list item views.

To connect data with views, the adapter needs to know about the views into which it will place the data. Therefore, the adapter contains a view holder (from the ViewHolder class) that describes an item view and its position within the RecyclerView.

In this task you will build an adapter with a view holder that bridges the gap between the data in your word list and the RecyclerView that displays it.

  1. Right-click java/com.android.example.recyclerview and select New > Java Class.
  2. Name the class WordListAdapter.
  3. Give WordListAdapter the following signature:
    public class WordListAdapter extends
        RecyclerView.Adapter<WordListAdapter.WordViewHolder>  {}
    
    WordListAdapter extends a generic adapter for RecyclerView to use a view holder that is specific for your app and defined inside WordListAdapter. WordViewHolder shows an error, because you have not defined it yet.
  4. Click on the class declaration (WordListAdapter) and then click on the red light bulb on the left side of the pane. Choose Implement methods. This brings up a dialog box that asks you to choose which methods to implement. Select all three methods and click OK.
    This creates empty placeholders for all the methods that you must implement. Note how onCreateViewHolder and onBindViewHolder both reference the WordViewHolder, which hasn't been implemented yet.

3.5 Create the view holder

  1. Inside the WordListAdapter class, add a new WordViewHolder inner class with this signature:
    class WordViewHolder extends RecyclerView.ViewHolder {}
    
  2. You will see an error about a missing default constructor. You can see details about the errors by hovering your mouse cursor over the red-underlined source code or over any red horizontal line on the right margin of the open-files pane.
  3. Add variables to the WordViewHolder inner class for the text view and the adapter:
    public final TextView wordItemView;
    final WordListAdapter mAdapter;
    
  4. In the inner class WordViewHolder, add a constructor that initializes the view holder's text view from the XML resources and sets its adapter:
    public WordViewHolder(View itemView, WordListAdapter adapter) {
       super(itemView);
       wordItemView = (TextView) itemView.findViewById(R.id.word);
       this.mAdapter = adapter;
    }
    
  5. Run your app to make sure you have no errors. Your will still see only a blank view. Take note of the E/RecyclerView: No adapter attached; skipping layout warning in logcat.

3.6 Storing your data in the adapter

  1. To hold your data in the adapter, create a private linked list of strings in WordListAdapter and call it mWordList.
    private final LinkedList<String> mWordList;
    
  2. You can now fill in the getItemCount() method to return the size of mWordList.
    @Override
    public int getItemCount() {
       return mWordList.size();
    }
    
    Next, WordListAdapter needs a constructor that initializes the word list from the data. To create a view for a list item, the WordListAdapter needs to inflate the XML for a list item. You use a layout inflater for that job. A LayoutInflator reads a layout XML description and converts it into the corresponding views.
  3. Create a member variable for the inflater in WordListAdapter.
    private LayoutInflater mInflater;
    
  4. Implement the constructor for WordListAdapter. The constructor needs to have have a context parameter, and a linked list of words with the app's data. The method needs to instantiate a layout inflater for mInflater and set mWordList to the passed in data.
    public WordListAdapter(Context context, LinkedList<String> wordList) {
       mInflater = LayoutInflater.from(context);
       this.mWordList = wordList;
    }
    
  5. Fill out the onCreateViewHolder() method with the code below. The onCreateViewHolder method is similar to the onCreate method. It inflates the item layout, and returns a view holder with the layout and the adapter.
    @Override
    public WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View mItemView = mInflater.inflate(R.layout.wordlist_item, parent, false);
       return new WordViewHolder(mItemView, this);
    }
    
  6. Fill out the onBindViewHolder method with the code below. The onBindViewHolder method connects your data to the view holder.
    @Override
    public void onBindViewHolder(WordViewHolder holder, int position) {
       String mCurrent = mWordList.get(position);
       holder.wordItemView.setText(mCurrent);
    }
    
  7. Run your app to make sure there are no errors. You will still see the "E/RecyclerView: No adapter attached; skipping layout" warning. You will fix that in the next task.

3.7. Create the RecyclerView in the Main Activity

Now that you have an adapter with a view holder, you can finally create a RecyclerView and connect all the pieces to display your data.

  1. Open MainActivity.java
  2. Add member variables to MainActivity for the RecyclerView and the adapter.
    private RecyclerView mRecyclerView;
    private WordListAdapter mAdapter;
    
  3. In the onCreate method of MainActivity, add the following code that creates the RecyclerView and connects it with an adapter and the data. Read the code comments! Note that you must insert this code after the mWordList initialization.
    // Get a handle to the RecyclerView.
    mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    // Create an adapter and supply the data to be displayed.
    mAdapter = new WordListAdapter(this, mWordList);
    // Connect the adapter with the RecyclerView.
    mRecyclerView.setAdapter(mAdapter);
    // Give the RecyclerView a default layout manager.
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    
  4. Run your app. You should see your list of words displayed, and you can scroll the list.

Task 4. Make the list interactive

Looking at lists of items is interesting, but it's a lot more fun and useful if your user can interact with them.

To see how the RecyclerView can respond to user input, you will programmatically attach a click handler to each item. When the item is tapped, the click handler is executed, and that item's text will change.

4.1. Make items respond to clicks

  1. Open the WordListAdapter.java file.
  2. Change the WordViewHolder class signature to implement View.onClickListener.
    class WordViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
    
  3. Click on the class header and on the red light bulb to implement stubs for the required methods, which in this case is just the onClick() method.
  4. Add the following code to the body of the onClick() method.
    // Get the position of the item that was clicked.
    int mPosition = getLayoutPosition();
    // Use that to access the affected item in mWordList.
    String element = mWordList.get(mPosition);
    // Change the word in the mWordList.
    mWordList.set(mPosition, "Clicked! " + element);
    // Notify the adapter, that the data has changed so it can
    // update the RecyclerView to display the data.
    mAdapter.notifyDataSetChanged();
    
  5. Connect the onClickListener with the view. Add this code to the WordViewHolder constructor (below the "this.mAdapter = adapter" line):
    itemView.setOnClickListener(this);
    
  6. Run your app. Click on items to see their text change.

Solution code: WordListAdapter.java and MainActivity.java

Task 5. Add a FAB to insert items

There are multiple ways in which you can add additional behaviors to the list and list items. One way is to use a floating action button (FAB). For example, in Gmail, the FAB is used to compose a new email. In this task you will implement a FAB to add an item to the word list.

Why? You have already seen that you can change the content of list items. The list of items that a RecyclerView displays can be modified dynamically-- it's not just a static list of items.

For this practical, you will generate a new word to insert into the list. For a more useful application, you would get data from your users.

5.1. Add a Floating Action Button (FAB)

The FAB is a standard control from the Material Design Specification and is part of the Android Design Support Library. You will learn more in the chapter about Material Design. These UI controls have predefined properties. To create a FAB for your app, add the following code inside the coordinator layout of activity_main.xml.

<android.support.design.widget.FloatingActionButton
   android:id="@+id/fab"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="bottom|end"
   android:layout_margin="16dp"
   android:clickable="true"
   android:src="@drawable/ic_add_24dp" />

Note the following:

  • <code>@+id/fab </code>It is customary to give the FAB the "fab" id.
  • android:layout_gravity="bottom|end". The FAB is commonly placed at the bottom and at the end of the reading/writing flow.
  • android:src="@drawable/ic_add_black_24dp". Is marked red by Android Studio because the resource is missing.

Android provides an icon library for standard Android icons. ic_add_black_24dp is one of the standard icons. You have to add it to your drawable resources to use it.

  1. Right-click your drawable folder.
  2. Select New > Vector Asset
  3. Make sure the Asset Type is Material Icon.
  4. Click the icon button next to Icon.
  5. In the Content section find the + sign. The resource name is ic_add_black_24dp.
  6. Leave everything else unchecked and click Next.
  7. Click Finish.
  8. Run your app.
    Note: Because this is a vector drawing, it is stored as an XML file. Vector drawings are automatically scaled, so you do not need to keep around a bitmap for each screen resolution. Learn more: Android Vector Asset Studio.

5.2. Add behavior to the FAB

In this task you'll add to the FAB an onClick listener that does the following:

  • Adds a word to the end of the list of words.
  • Notifies the adapter that the data has changed.
  • Scrolls to the inserted item.
  • In MainActivity.java, at the end of the onCreate() method, add the following code:
    // Add a floating action click handler for creating new entries.
    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
           int wordListSize = mWordList.size();
           // Add a new word to the end of the wordList.
           mWordList.addLast("+ Word " + wordListSize);
           // Notify the adapter, that the data has changed so it can
           // update the RecyclerView to display the data.
           mRecyclerView.getAdapter().notifyItemInserted(wordListSize);
           // Scroll to the bottom.
           mRecyclerView.smoothScrollToPosition(wordListSize);
        }
    });
    
  • Run your app. To test your app do the following:
    1. Scroll the list of words.
    2. Click on items.
    3. Add items by clicking on the FAB.
    4. What happens if you rotate the screen? You will learn in a later lesson how to preserve the state of an app when the screen is rotated.

Solution code

Android Studio project: RecyclerView

Coding challenge

Note: All coding challenges are optional and are not prerequisites for later lessons.

Challenge: Creating a click listener for each item in the list is easy, but it can hurt the performance of your app if you have a lot of data. Research how you could implement this more efficiently. This is an advanced challenge. Start by thinking about it conceptually, and then search for an implementation example.

Summary

  • RecyclerView is a resource-efficient way to display a scrollable list of items.
  • To use RecyclerView, you associate the data to the Adapter/ViewHolder that you create and to the layout manager of your choice.
  • Click listeners can be created to detect mouse clicks in a RecyclerView.
  • Android support libraries contain backward-compatible versions of the Android framework.
  • Android support libraries contain a range of useful utilities for your apps.
  • Build dependencies are added to the build.gradle (Module app) file.
  • Layouts can be specified as a resource file.
  • A LayoutInflater reads a layout resource file and creates the View objects from that file.
  • A Floating Action Button (FAB) can dynamically modify the items in a RecyclerView.

The related concept documentation is in Android Developer Fundamentals: Concepts.

Learn more

results matching ""

    No results matching ""