Pagination with Recyclerview in android

What is Pagination with Recyclerview in android?

Pagination with Recyclerview allows the user to see the latest content as page by page. As we load the next ‘page’ by the time users scroll to the bottom, more content is loaded and available.

pagination recyclerview

When to use Pagination?

I’m sure you have a pretty good idea by now on when to use it. If you have a ton of content that takes too long to load. This can be either from a local database or an API call. Then it makes sense to use Pagination. If you’re pulling from a database, request data in batches (say 20 per request). The same also holds true for an API call.

What is Recyclerview?

Android RecyclerView is a more advanced, powerful and flexible version of the ListView. Android RecyclerView is similar to ListView except that it forces us to use RecyclerView.ViewHolder class to hold the elements which are not a compulsion in ListView.

As the name suggests, Android RecyclerView is used to reuse cells when scrolling up and down by recycling the items in the list. Another improvement in RecyclerView is that it allows us to set the LayoutManagers dynamically at runtime, unlike the ListView which was only available in a Vertical scrolling List. RecyclerView allows us to set the following types of Layouts at runtime.

LinearLayoutManager: it supports both vertical and horizontal lists.
StaggeredLayoutManager: it supports staggered lists.
GridLayoutManager: it supports displaying grids as seen in GalleryView earlier.


Related: Android Recyclerview Material Design with Example
Related: Retrofit Android Example With Recyclerview


Setup Retrofit

First, We have to add the retrofit dependency into our build.grade file. We can find the latest retrofit version in the official retrofit website. http://square.github.io/retrofit/

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Add Internet Permission to your Application in AndroidManifest.xml,

<uses-permission android:name="android.permission.INTERNET"/>

Create the Retrofit instance

We need to create the Retrofit instance to send the network requests. we need to use the Retrofit Builder class and specify the base URL for the service.

ClientApi.java

public class ClientApi {

    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("http://35.200.174.74/apis/")
                    .build();
        }
        return retrofit;
    }

}

Setting Up the Retrofit Interface

Retrofit provides the list of annotations for each HTTP methods:

@GET, @POST, @PUT, @DELETE, @PATCH or @HEAD.

The endpoints are defined inside of an interface using retrofit annotations to encode details about the parameters and request method. T return value is always a parameterized Call<T>.

MovieService.java

public interface MovieService {
    @GET("volley_array.json")
    Call<List<Movie>> getMovies();
}

In my JSON response, I am having the list of movies with name, properties. So, My Model class will be like Movie as class name and name, year, director are properties.

Movie.java

public class Movie {

    @SerializedName("title")
    private String title;

    @SerializedName("image")
    private String imageUrl;

    public Movie(){}

    public Movie(String title, String imageUrl) {
        this.title = title;
        this.imageUrl = imageUrl;
    }

    public String getTitle() {
        return title;
    }

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

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

Setup Recyclerview

For using RecyclerView in your project you need to add the recycler view support library to your project. Add the following gradle dependency to project build.gradle file.

compile 'com.android.support:recyclerview-v7:27.1.1'

Add android.support.v7.widget.RecyclerView into activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    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"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingBottom="24dp"/>

    <ProgressBar
        android:id="@+id/progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</FrameLayout>

Related: Glide Android – Image loading framework for Android
Related: shimmer effect For Android Recyclerview 


Pagination with RecyclerView

To enable Pagination, we must detect the user reaching the end of the RecyclerView items. For that, PaginationScrollListener extends the RecyclerView.OnScrollListener and override the onScrolled() method.

PaginationScrollListener.java

public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {

    private LinearLayoutManager layoutManager;

    public PaginationScrollListener(LinearLayoutManager layoutManager) {
        this.layoutManager = layoutManager;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        int visibleItemCount = layoutManager.getChildCount();
        int totalItemCount = layoutManager.getItemCount();
        int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();

        if (!isLoading() && !isLastPage()) {
            if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
                    && firstVisibleItemPosition >= 0) {
                loadMoreItems();
            }
        }
    }
}

Recyclerview Adapter for Pagination

First, create class PaginationAdapter extending RecyclerView.Adapter, and then create two RecyclerView.ViewHolder.

class MovieViewHolder() – Recyclerview items

class LoadingViewHolder() – Footer ProgressBar used for Pagination

PaginationAdapter.java

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

    private Context context;
    private List<Movie> movieList;
    private static final int LOADING = 0;
    private static final int ITEM = 1;
    private boolean isLoadingAdded = false;

    public PaginationAdapter(Context context) {
        this.context = context;
        movieList = new LinkedList<>();
    }

    public void setMovieList(List<Movie> movieList) {
        this.movieList = movieList;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder viewHolder = null;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        switch (viewType) {
            case ITEM:
                View viewItem = inflater.inflate(R.layout.item_list, parent, false);
                viewHolder = new MovieViewHolder(viewItem);
                break;
            case LOADING:
                View viewLoading = inflater.inflate(R.layout.item_progress, parent, false);
                viewHolder = new LoadingViewHolder(viewLoading);
                break;
        }
        return viewHolder;
    }

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

        Movie movie = movieList.get(position);
        switch (getItemViewType(position)) {
            case ITEM:
                MovieViewHolder movieViewHolder = (MovieViewHolder) holder;
                movieViewHolder.movieTitle.setText(movie.getTitle());
                Glide.with(context).load(movie.getImageUrl()).apply(RequestOptions.centerCropTransform()).into(movieViewHolder.movieImage);
                break;

            case LOADING:
                LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
                loadingViewHolder.progressBar.setVisibility(View.VISIBLE);
                break;
        }
    }

    @Override
    public int getItemCount() {
        return movieList == null ? 0 : movieList.size();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == movieList.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
    }

    public void addLoadingFooter() {
        isLoadingAdded = true;
        add(new Movie());
    }

    public void removeLoadingFooter() {
        isLoadingAdded = false;

        int position = movieList.size() - 1;
        Movie result = getItem(position);

        if (result != null) {
            movieList.remove(position);
            notifyItemRemoved(position);
        }
    }

    public void add(Movie movie) {
        movieList.add(movie);
        notifyItemInserted(movieList.size() - 1);
    }

    public void addAll(List<Movie> moveResults) {
        for (Movie result : moveResults) {
            add(result);
        }
    }

    public Movie getItem(int position) {
        return movieList.get(position);
    }


    public class MovieViewHolder extends RecyclerView.ViewHolder {

        private TextView movieTitle;
        private ImageView movieImage;

        public MovieViewHolder(View itemView) {
            super(itemView);
            movieTitle = (TextView) itemView.findViewById(R.id.movie_title);
            movieImage = (ImageView) itemView.findViewById(R.id.movie_poster);
        }
    }

    public class LoadingViewHolder extends RecyclerView.ViewHolder {

        private ProgressBar progressBar;

        public LoadingViewHolder(View itemView) {
            super(itemView);
            progressBar = (ProgressBar) itemView.findViewById(R.id.loadmore_progress);

        }
    }


}

Let’s Bring Everything Together in your MainActivity.java

Add Adapter to Recyclerview

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
paginationAdapter = new PaginationAdapter(this);
recyclerView.setAdapter(paginationAdapter);

Load first-page item when lunch the screen,

private void loadFirstPage() {

        movieService.getMovies().enqueue(new Callback<List<Movie>>() {
            @Override
            public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
                List<Movie> results = response.body();
                progressBar.setVisibility(View.GONE);
                paginationAdapter.addAll(results);

                if (currentPage <= TOTAL_PAGES) paginationAdapter.addLoadingFooter();
                else isLastPage = true;
            }

            @Override
            public void onFailure(Call<List<Movie>> call, Throwable t) {

            }

        });
    }

Attach pagination to Recyclerview and load items when scroll,

recyclerView.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
            @Override
            protected void loadMoreItems() {
                isLoading = true;
                currentPage += 1;
                loadNextPage();
            }

            @Override
            public boolean isLastPage() {
                return isLastPage;
            }

            @Override
            public boolean isLoading() {
                return isLoading;
            }
        });


private void loadNextPage() {

    movieService.getMovies().enqueue(new Callback<List<Movie>>() {
        @Override
        public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
            paginationAdapter.removeLoadingFooter();
            isLoading = false;

            List<Movie> results = response.body();
            paginationAdapter.addAll(results);

            if (currentPage != TOTAL_PAGES) paginationAdapter.addLoadingFooter();
            else isLastPage = true;
        }

        @Override
        public void onFailure(Call<List<Movie>> call, Throwable t) {
            t.printStackTrace();
        }
    });
}

2 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top
%d bloggers like this: