Use pagination in android RecyclerView , ListView,GridView or any other list.

What is pagination?

Pagination means load data in some chunks called as pagination. For example load 100 items in 10 parts having 10 item each, called as pagination.

Why we need pagination?

If you are loading 10,000 items from Web API or local database then it will take more time to fetch data from Web API and display them in List. So its better to display first 10 items, and if user want to view more then load 10 more and so on. This will increase load time of data and performance of app too.

In this example I’m using Youtube Data API, you can use any API which you preffer. And I’m using RecyclerView to display items, you can use ListView or GridView too.

Main Idea behind this Logic :

In Youtube API we get token (String) of next list, so we will pass that token while requesting next data call. But first time we will pass empty value as token so it will return latest items.

But if you want to use Pagination in your own web API then ask your Backend developer to add 2 request parameters “start” and “limit”. And return “last_index” along with data. “start” parameter will be used to fetch data from “start” index and “limit” parameter will be used to max number of data.

For Example :

if you pass 0 as “start” and 10 as “limit” parameter then API will return data from 0 to 10 items. And now in next call pass “last_index + 1” as start parameter. So now you will pass 11 as “start” and 10 as “limit” and you will get data from 11 to 20 and so on.

Now when you get data from Web API then add that data to adapter of RecyclerView, ListView or GridView. You can also replace data of Adapter if you want to display only new items.

code of MainActivity.java

package com.learnpainless.paginationrecyclerview;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.learnpainless.paginationrecyclerview.adapters.VideosAdapter;
import com.learnpainless.paginationrecyclerview.api.APIService;
import com.learnpainless.paginationrecyclerview.models.YoutubeResponse;
import com.learnpainless.paginationrecyclerview.utils.Constants;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private VideosAdapter adapter;
    //if you are using searchview to get search result then store searched query in lastSearched variable.
    //get latest token and store in lastToken variable.
    private String lastSearched = "", lastToken = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = findViewById(R.id.video_list);
        Button more = findViewById(R.id.more);

        adapter = new VideosAdapter();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
        recyclerView.setAdapter(adapter);

        //load data from api.
        search("", false);

        more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //load more data
                search("", true);
            }
        });
    }

    /**
     * call this method to get response from youtube API.
     *
     * @param query String value to search on google, Empty string means get all videos.
     * @param more  if you want to load next page then pass true, this means add new items at bottom of RecyclerView.
     */
    private void search(String query, final boolean more) {
        final ProgressDialog progressDialog = ProgressDialog.show(this, null, "Loading ...", true, false);
        String searchType = "video";
        if (query != null) {
            if (query.startsWith("#")) {
                searchType = "video";
                query = query.substring(1);
            } else if (query.startsWith("@")) {
                searchType = "channel";
                query = query.substring(1);
            }
        }
        if (!more) {
            lastSearched = query;
            lastToken = "";
        }

        Call<YoutubeResponse> youtubeResponseCall = APIService.youtubeApi.searchVideo(query, searchType, Constants.YOUTUBE_API_KEY, "snippet,id", "10", lastToken);
        youtubeResponseCall.enqueue(new Callback<YoutubeResponse>() {
            @Override
            public void onResponse(@NonNull Call<YoutubeResponse> call, @NonNull Response<YoutubeResponse> response) {
                if (progressDialog.isShowing()) {
                    progressDialog.dismiss();
                }
                YoutubeResponse body = response.body();
                if (body != null) {
                    List<YoutubeResponse.Item> items = body.getItems();
                    lastToken = body.getNextPageToken();
                    if (more) {
                        adapter.addAll(items);
                    } else {
                        adapter.replaceWith(items);
                    }
                }
            }

            @Override
            public void onFailure(@NonNull Call<YoutubeResponse> call, @NonNull Throwable t) {
                if (progressDialog.isShowing()) {
                    progressDialog.dismiss();
                }
                Log.e(TAG, "onFailure: ", t);
            }
        });

    }
}

code of activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.learnpainless.paginationrecyclerview.MainActivity">

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

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

    <Button
        android:id="@+id/more"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="Load More" />

</RelativeLayout>

code of APIService.java

package com.learnpainless.paginationrecyclerview.api;

import com.learnpainless.paginationrecyclerview.utils.Constants;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by pawneshwer on 9/9/17.
 * Retrofit's base service builder class.
 */

public class APIService {
    private static <LPL> LPL createService(Class<LPL> cls) {
        Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(Constants.BASE_URL).build();
        return retrofit.create(cls);
    }

    public static final YoutubeApi youtubeApi = createService(YoutubeApi.class);
}

code of YoutubeApi.java

package com.learnpainless.paginationrecyclerview.api;

import com.learnpainless.paginationrecyclerview.models.YoutubeResponse;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
 * Created by pawneshwer on 9/9/17.
 * Retrofit API class to call APIs.
 */

public interface YoutubeApi {
    @GET("search")
    Call<YoutubeResponse> searchVideo(@Query("q") String query,
                                  @Query("type") String type,
                                  @Query("key") String key,
                                  @Query("part") String part,
                                  @Query("maxResults") String maxResults,
                                  @Query("pageToken") String pageToken);
}

code of YoutubeResponse.java

package com.learnpainless.paginationrecyclerview.models;

import com.google.gson.annotations.SerializedName;

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

/**
 * Created by pawneshwer on 9/9/17.
 * This class will parse data from youtube API.
 */

public final class YoutubeResponse {
    private String kind, etag, nextPageToken;
    private PageInfo pageInfo;
    private List<Item> items = new ArrayList<>();

    public String getKind() {
        return kind;
    }

    public String getEtag() {
        return etag;
    }

    public String getNextPageToken() {
        return nextPageToken;
    }

    public PageInfo getPageInfo() {
        return pageInfo;
    }

    public List<Item> getItems() {
        return items;
    }

    public static class PageInfo {
        private int totalResults, resultsPerPage;

        public int getTotalResults() {
            return totalResults;
        }

        public int getResultsPerPage() {
            return resultsPerPage;
        }
    }

    public static class Item {
        private String kind, etag;
        private Snippet snippet;

        public String getKind() {
            return kind;
        }

        public String getEtag() {
            return etag;
        }

        public Snippet getSnippet() {
            return snippet;
        }

        public static class Snippet {
            private String publishedAt, channelId, title, description, channelTitle, categoryId, liveBroadcastContent;
            private Thumbnails thumbnails;
            private List<String> tags = new ArrayList<>();

            public String getPublishedAt() {
                return publishedAt;
            }

            public String getChannelId() {
                return channelId;
            }

            public String getTitle() {
                return title;
            }

            public String getDescription() {
                return description;
            }

            public String getChannelTitle() {
                return channelTitle;
            }

            public String getCategoryId() {
                return categoryId;
            }

            public String getLiveBroadcastContent() {
                return liveBroadcastContent;
            }

            public Thumbnails getThumbnails() {
                return thumbnails;
            }

            public List<String> getTags() {
                return tags;
            }

            public static class Thumbnails {
                @SerializedName("default")
                private Default _default;

                public Default getDefault() {
                    return _default;
                }

                public static final class Default {
                    private String url;
                    private int width, height;

                    public String getUrl() {
                        return url;
                    }

                    public int getWidth() {
                        return width;
                    }

                    public int getHeight() {
                        return height;
                    }
                }
            }
        }
    }


}

code of VideosAdapter.java

package com.learnpainless.paginationrecyclerview.adapters;

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.bumptech.glide.Glide;
import com.learnpainless.paginationrecyclerview.R;
import com.learnpainless.paginationrecyclerview.models.YoutubeResponse;

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

/**
 * Created by pawneshwer on 9/9/17.
 * Adapter implementation for {@link RecyclerView}
 */

public class VideosAdapter extends RecyclerView.Adapter<VideosAdapter.VideoViewHolder> {
    private List<YoutubeResponse.Item> items = new ArrayList<>();

    @Override
    public VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_item_video, parent, false);
        return new VideoViewHolder(view);
    }

    @Override
    public void onBindViewHolder(VideoViewHolder holder, int position) {
        YoutubeResponse.Item.Snippet snippet = items.get(position).getSnippet();
        String url = snippet.getThumbnails().getDefault().getUrl();
        Glide.with(holder.itemView.getContext()).load(url).crossFade().centerCrop().into(holder.logo);

        holder.title.setText(snippet.getTitle());
        holder.description.setText(snippet.getDescription());
        holder.date.setText(snippet.getPublishedAt());
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void addAll(Collection<YoutubeResponse.Item> items) {
        int currentItemCount = this.items.size();
        this.items.addAll(items);
        notifyItemRangeInserted(currentItemCount, items.size());
    }

    public void replaceWith(Collection<YoutubeResponse.Item> items) {
        if (items != null) {
            int oldCount = this.items.size();
            int newCount = items.size();
            int delCount = oldCount - newCount;
            this.items.clear();
            this.items.addAll(items);
            if (delCount > 0) {
                notifyItemRangeChanged(0, newCount);
                notifyItemRangeRemoved(newCount, delCount);
            } else if (delCount < 0) {
                notifyItemRangeChanged(0, oldCount);
                notifyItemRangeInserted(oldCount, -delCount);
            } else {
                notifyItemRangeChanged(0, newCount);
            }
        }

    }

    static class VideoViewHolder extends RecyclerView.ViewHolder {
        ImageView logo;
        TextView title, description, date;

        VideoViewHolder(View itemView) {
            super(itemView);
            logo = itemView.findViewById(R.id.logo);
            title = itemView.findViewById(R.id.title);
            description = itemView.findViewById(R.id.description);
            date = itemView.findViewById(R.id.date);
        }
    }
}

star this repo fork this repo

Download APK