In this RecyclerView tutorial we’ll be creating an Android application which fetch popular movies from an API provided by TMDB. You can jump straight to the code by clicking¬†here.

I am writing this article after having taken an Android developer course over at Edureka. It is a great course, which I highly recommend. It was even recently certified by Google itself.. ūüôā

Prior to the RecyclerView in Android, one almost always used the ListView.
The Android RecyclerView is an improved and more flexible version of the ListView, and comes with greater performance, which has proven to be great for large data sets.

As a rule of thumb, whenever you want to present large data sets or data that frequently changes through a list, you should make use of the RecyclerView.

What is the RecyclerView in Android?

Referencing the Google Developer documentation, the RecyclerView is “a¬†flexible view for providing a limited window into a large data set.”
Translated to plain english, this simply means that the RecyclerView is a view you would use whenever you want to present data in a list.

What makes the RecyclerView better for performance than its predecessor (the ListView), is in the way that the elements in the list is rendered. We’ll be illustrating this with an example where you have a list containing 50 objects. Keep in mind that all the elements in a RecyclerView is connected to a¬†ViewHolder. We’ll elaborate on this later!

Now, assume that the user scrolls down through the list. When the list is scrolled, the views that move off screen (outside of the layout-container), will automatically be saved. If our user wants to scroll back up, the previously saved elements can be used, instead of instantiating new ViewHolders.

The RecyclerView will automatically save elements which has been scrolled of screen, so that they can be reused later.

One other feature of the RecyclerView in Android which makes it really powerful,  is the reuse of ViewHolders. If we assume that our user keeps scrolling through the list (and not going back up), the previously instantiated ViewHolders will be reused when presenting new elements.

The RecyclerView will reuse it’s ViewHolders when the user scrolls down through the list. This minimizes the amount of memory needed to present large data sets.

Now that we have a general understanding of how the RecyclerView works, let’s go about implementing it.

We’ll implement the RecyclerView in a sample application which shows a list of movies, which are fetched through the TMDB API.

You can apply for an API key by clicking the “API” link from the left hand sidebar within your account settings page. You need to have a legitimate business name, address, phone number and description to apply for an API key.
If you don’t have a business name, simply fill in your initials. I did this, and my application got accepted right away.

Using Volley and RecyclerView in Android

The first thing we’ll do is to create an empty Android Studio Project. Fire up Android Studio, and do the following:

File –> New –> New Project

After this, you can simply enter the same configuration as described in the images below.

Creating a new Android Project for a RecyclerView
Setting project and artifact name
RecyclerView project example in Android
Setting minimum SDK to Android 23
Choosing setup for a RecyclerView in Android
Initializing our application with an empty activity
Creating android recyclerview example
Autogenerating a MainActivity.java and corresponding layout

Now, hit¬†finish,¬†and you’re ready to start developing a real life android application containing a RecyclerView.

The first thing we’ll do is to create packages, to make our source code organized.¬†The packages we’ll need are the following:

  • model:¬†This is where we will be placing all classes which represents entities from the API (like a movie).
  • loader:¬†This package will contain classes responsible for fetching and reading data from the API.
  • activities:¬†Package containing all activities.
  • adapter:¬†This is the package where we place the adapter for our RecyclerView.

After creating the packages and placing MainActivity under the activities package, your project structure should look like this:

Project Structure of the RecyclerView example application

First of all, we need to be able to connect to the Internet through the application. To be able to do so, add this to your AndroidManifest.xml:

<!-- Internet connection -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Our application will also need some extra added dependencies. Simple add the following dependencies to your build.gradle file:

    
    // Android Support Library
    implementation 'com.android.support:design:27.1.1'
    implementation 'com.android.support:cardview-v7:27.1.1'
    // 3rd party, Picasso. Used to loads images.
    // http://square.github.io/picasso/
    implementation 'com.squareup.picasso:picasso:2.5.2'
    // Volley, API calls
    implementation 'com.android.volley:volley:1.1.0'

Before we dive into the code of the RecyclerView, we create the other necessary classes. If you’re only interested in the RecyclerView, you can simply copy / paste everything outside of the adapter package.

We’ll start off by creating a¬†Movie¬†class in the¬†model¬†package.

package modestprogrammer.com.movietestapplication.model;

/**
 * POJO representing a Movie, which will be downloaded from the TMDB API.
 *
 * @author Anders Engen Olsen.
 */
public class Movie {

    // Base url for the posters
    private final String POSTER_BASE_URL = "https://image.tmdb.org/t/p/w185";

    // The ID from TMDB
    private int id;
    // Title of the movie
    private String title;
    // Rating of the movie
    private String rating;
    // Poster for the movie
    private String posterUrl;


    public Movie(int id, String title, String rating, String posterUrl) {
        this.id = id;
        this.title = title;
        this.rating = rating;
        this.posterUrl = POSTER_BASE_URL + posterUrl;
    }

    /* -- GETTERS AND SETTERS -- */

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getRating() {
        return rating;
    }

    public void setRating(String rating) {
        this.rating = rating;
    }

    public String getPosterUrl() {
        return posterUrl;
    }

    public void setPosterUrl(String posterUrl) {
        this.posterUrl = posterUrl;
    }
}

Now that we’ve created a class representing a movie, we need a way to download the movies from the API. We’ll do this by using the Volley HTTP library. We will not go into the details on how to use Volley. However, if you want to learn how to use it, you can read more about it in my Android Volley HTTP library tutorial.

Let’s create the class¬†TmdbLoader, which will download popular movies from the API, and place them in an ArrayList. The TmdbLoader class will also contain a local interface, which will be used to pass the downloaded movies back to the calling activity. Note that the documentation for the TMDB API can be found here.

package modestprogrammer.com.movietestapplication.loader;

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

import modestprogrammer.com.movietestapplication.model.Movie;

/**
 * Class responsible for downloading data from the TMDB API.
 *
 * @author Anders Engen Olsen
 */
public class TmdbLoader {

    /**
     * The interface which will be notified when the movies are downloaded
     */
    public interface TmdbListener<AnyType> {
        void onMovieDownloaded(AnyType a);

        void onErrorDownloading(String errorMessage);
    }

    /**
     * Singleton instance of this class
     */
    private static TmdbLoader tmdbLoader = null;

    /**
     * RequestQueue Volley-library
     */
    protected RequestQueue queue;
    /**
     * Activity context
     */
    protected Context context;
    /**
     * API KEY
     */
    protected String apiKey = FILL IN YOUR API KEY HERE;

    /**
     * Private constructor. Called from getInstance().
     *
     * @param context Activity-context
     * @see #getInstance(Context)
     */
    private TmdbLoader(Context context) {
        this.context = context;
        queue = Volley.newRequestQueue(context);
    }

    /**
     * Initalizing a TmdbLoader object if null.
     *
     * @param context Activity-context
     * @return Singleton object
     */
    public static synchronized TmdbLoader getInstance(Context context) {
        if (tmdbLoader == null)
            tmdbLoader = new TmdbLoader(context);

        return tmdbLoader;
    }

    /**
     * Fetching popular movies
     *
     * @param listener listener, fired when downloaded
     */
    public void getPopularMovies(final TmdbListener<ArrayList<Movie>> listener) {

        // This is the base URL where the movies are downloaded from
        String url = "https://api.themoviedb.org/3/movie/popular";
        // Append the api key to the url
        Uri uri = Uri.parse(url).buildUpon()
                .appendQueryParameter("api_key", apiKey)
                .build();

        Log.d("loader", uri.toString());


        // Construction a JsonObjectRequest.
        // This means that we expect to receive a JsonObject from the API.
        final JsonObjectRequest json = new JsonObjectRequest(
                Request.Method.GET, uri.toString(), null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        // This indicates that the call was successfull.

                        // Creating an ArrayList with Movie-objects.
                        ArrayList<Movie> movies = parseMovies(response);

                        // Firing our interface-method, returning the movies to the activity
                        listener.onMovieDownloaded(movies);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // This indicates that the call wasn't successful.
                // We'll return a String to our activity
                listener.onErrorDownloading("Error connecting to the API");
            }
        }
        );

        // Adding the response to our requestqueue
        queue.add(json);
    }

    /**
     * Fetching movies from json provided by the TMDB API.
     *
     * @param jsonObject
     * @return ArrayList of movies
     */
    private ArrayList<Movie> parseMovies(JSONObject jsonObject) {
        try {
            // The arraylist with movies
            ArrayList<Movie> movies = new ArrayList<>();

            // Fetching the array of movie results
            JSONArray array = jsonObject.getJSONArray("results");

            // Looping through and creating the Movie objects
            for (int i = 0; i < array.length(); i++) {

                // Getting the current jsonobject
                JSONObject jo = array.getJSONObject(i);

                // Creating the Movie object
                int id = jo.getInt("id");
                String title = jo.getString("title");
                String rating = String.valueOf(jo.getInt("vote_average"));
                String posterUrl = jo.getString("poster_path");

                Movie movie = new Movie(id, title, rating, posterUrl);

                movies.add(movie);
            }

            return movies;
        } catch (JSONException err) {
            // Error occurred!
            return null;
        }
    }
}

Now that we’ve created the “back-end” of the application, let’s implement the¬†RecyclerView (finally!).

First of all, our application will simply display the movie list in the MainActivity¬†on startup. Let’s head over to res/activity_main.xml, and include a RecyclerView in the layout.

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

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

    </LinearLayout>
</ScrollView>

When creating a custom RecyclerView, one also has to provide a custom layout. Let’s add a custom layout to our project,¬†res/recycler_movie_list_item.xml.¬†This layout defines how each row in the RecyclerView will be designed.

<?xml version="1.0" encoding="utf-8"?>

<!-- Layout for the RecyclerViews -->

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/poster"
            android:layout_width="45dp"
            android:layout_height="80dp"
            android:layout_alignParentStart="true"
            android:padding="2dp"
            android:scaleType="fitXY" />

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@id/poster"
            android:paddingLeft="4dp"
            android:paddingRight="4dp" />

        <TextView
            android:id="@+id/rating"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/title"
            android:layout_toEndOf="@id/poster"
            android:paddingLeft="4dp"
            android:paddingRight="4dp"
            android:paddingTop="2dp" />
    </RelativeLayout>

</android.support.v7.widget.CardView>

We’re almost there! The next thing to do is to create a custom Adapter for the RecyclerView. The Adapter will tell which layout to use (recycler_movie_list_item.xml), and will also contain an inner class – which will be the ViewHolder.

package modestprogrammer.com.movietestapplication.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.util.ArrayList;

import modestprogrammer.com.movietestapplication.R;
import modestprogrammer.com.movietestapplication.model.Movie;

/**
 * Custom adapter for the RecyclerViews.
 *
 * @author Anders Engen Olsen
 */
public class MovieRecyclerAdapter extends
        RecyclerView.Adapter<RecyclerView.ViewHolder> {

    // The activity context
    private Context context;
    // The ArrayList of movies in the RecyclerView
    protected ArrayList<Movie> movieList;

    /**
     * Constructor.
     *
     * @param context   Activity context
     * @param movieList List with movies to show
     */
    public MovieRecyclerAdapter(Context context, ArrayList<Movie> movieList) {
        this.context = context;
        this.movieList = movieList;
    }

    /**
     * Initiating ViewHolder with layout.
     *
     * @return RecyclerImageViewHolder
     * @see RecyclerListHolder(View)
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.recycler_list_movie_item, parent, false);

        return new RecyclerListHolder(view);
    }

    /**
     * Setting content in Views in the ViewHolder.
     *
     * @param holder   ViewHolder
     * @param position position in adapter
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        RecyclerListHolder recyclerListHolder = (RecyclerListHolder) holder;

        Movie movie = movieList.get(position);

        recyclerListHolder.title.setText(movie.getTitle());
        recyclerListHolder.rating.setText(movie.getRating());

        Picasso.with(context).load(movie.getPosterUrl())
                .error(android.R.drawable.alert_dark_frame)
                .placeholder(android.R.drawable.alert_dark_frame)
                .into(recyclerListHolder.poster);
    }

    /**
     * @return int number of objects in adapter.
     */
    @Override
    public int getItemCount() {
        return (null != movieList ? movieList.size() : 0);
    }

    /**
     * Method to set a new movie list to be shown
     *
     * @param movieList movieList to show
     */
    public void setMovieList(ArrayList<Movie> movieList) {
        this.movieList = movieList;
        notifyDataSetChanged();
    }

    /**
     * Inner class, ViewHolder for the elements in the RecyclerView
     */
    private class RecyclerListHolder extends RecyclerView.ViewHolder {

        private ImageView poster;
        private TextView title;
        private TextView rating;

        /**
         * @param view Root
         */
        private RecyclerListHolder(View view) {
            super(view);

            poster = view.findViewById(R.id.poster);
            title = view.findViewById(R.id.title);
            rating = view.findViewById(R.id.rating);
        }
    }
}

That’s pretty much it. The inner class¬†RecyclerListHolder simply contains the Views in the layout we’ve created. The method #onBindViewHolder() binds the layout elements to the actual values in the Movie-objects.

The last thing we’ll need to do to get the application up and running is to update the¬†MainActivity.java. In the activity, we’ll bind the RecyclerView in¬†activity_main.xml¬†to our custom¬†MovieRecyclerAdapter, and download the movies with the¬†TmdbLoader¬†class.

package modestprogrammer.com.movietestapplication.activities;

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

import java.util.ArrayList;

import modestprogrammer.com.movietestapplication.R;
import modestprogrammer.com.movietestapplication.adapter.MovieRecyclerAdapter;
import modestprogrammer.com.movietestapplication.loader.TmdbLoader;
import modestprogrammer.com.movietestapplication.model.Movie;

public class MainActivity extends AppCompatActivity {

    // The loader class
    private TmdbLoader tmdbLoader;

    // The RecyclerView
    private RecyclerView moviesRecyclerView;

    // Our custom adapter for the RecyclerView
    private MovieRecyclerAdapter movieRecyclerAdapter;

    // The list downloaded from the API
    private ArrayList<Movie> movieList;

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

        // Instantiating the loader and the list
        tmdbLoader = TmdbLoader.getInstance(this);
        movieList = new ArrayList<>();

        setUpRecycler();
        downloadPopularMovies();
    }

    /**
     * Setting up the recyclerview
     */
    private void setUpRecycler() {
        // Connecting the recyclerview to the view in the layout
        moviesRecyclerView = findViewById(R.id.recycler_main_movie);

        // Creating our custom adapter
        movieRecyclerAdapter = new MovieRecyclerAdapter(this, movieList);

        // Setting the adapter to our recyclerview
        moviesRecyclerView.setAdapter(movieRecyclerAdapter);

        // Creating and setting a layout manager.
        // Note that the manager is VERTICAL, thus a vertical list
        LinearLayoutManager layout = new LinearLayoutManager(
                this, LinearLayoutManager.VERTICAL, false);
        moviesRecyclerView.setLayoutManager(layout);

    }

    /**
     * Downloading popular movies, and notifying the adapter when the list is downloaded.
     */
    private void downloadPopularMovies() {
        tmdbLoader.getPopularMovies(new TmdbLoader.TmdbListener<ArrayList<Movie>>() {
            @Override
            public void onMovieDownloaded(ArrayList<Movie> result) {
                movieList = result;

                // Setting the list to the adapter.
                // This will cause the list to be presented in the layout!
                movieRecyclerAdapter.setMovieList(result);
            }

            @Override
            public void onErrorDownloading(String errorMessage) {

            }
        });

    }
}

Again, if you wish to take a deep dive into Android programming, I recommend checking out the Android developer course hosted by Edureka. If you don’t feel like paying for a course, you can always head over to Google and go through their documentation, too!

That’s it!¬†You now have a fully working application which shows the 20 most popular movies on TheMovieDB.org !