Pagination In RecyclerView Using RxJava Operators

Authors
  • Amit Shekhar
    Name
    Amit Shekhar
    Published on
Pagination In RecyclerView Using RxJava Operators

I am Amit Shekhar, a mentor helping developers in getting high-paying tech jobs.

In this blog, we will learn how to implement pagination in RecyclerView using RxJava operators.

Nowadays, most of the applications that we use in our daily life come with a scrolling feature. They fetch data as we scroll down more from the data sources like networks, databases, etc. They load the data on demand. As we scroll down it loads more data.

So, having the scrolling feature in the Android application is very common. And, we as a developer have the responsibility to implement the pagination in a better way.

Let’s see how to implement it in a better way using the RxJava Operators.

Don’t forget: There is an operator for everything in RxJava.

I believe that we can easily solve any complex problem with RxJava which can be very difficult without RxJava. RxJava is just awesome.

Let’s jump directly into implementing the Pagination in RecyclerView using the RxJava Operators.

First of all, create a simulation of the data source for the example purpose.

public class PaginationActivity extends AppCompatActivity {
    /**
     * Simulation of network data
     */
    private Single<List<String>> dataFromNetwork(final int page) {
        return Single.just(true)
                .delay(2, TimeUnit.SECONDS)
                .map(value -> {
                    List<String> items = new ArrayList<>();
                    for (int i = 1; i <= 10; i++) {
                        items.add("Item " + (page * 10 + i));
                    }
                    return items;
                });
    }
}

Then, create a very simple adapter for the RecyclerView.

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

    List<String> items = new ArrayList<>();

    public PaginationAdapter() {

    }

    void addItems(List<String> items) {
        this.items.addAll(items);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return ItemViewHolder.create(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((ItemViewHolder) holder).bind(items.get(position));
    }

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

    private static class ItemViewHolder extends RecyclerView.ViewHolder {
        ItemViewHolder(View itemView) {
            super(itemView);
        }

        static ItemViewHolder create(ViewGroup parent) {
            return new ItemViewHolder(
                    LayoutInflater.from(parent.getContext()).inflate(R.layout.item_pagination, parent, false));
        }

        void bind(String content) {
            ((TextView) itemView).setText(content);
        }
    }
}

Then set up the loadMoreListener on the RecyclerView and subscribe for the data.

public class PaginationActivity extends AppCompatActivity {

    // removed for brevity..

    private PublishProcessor<Integer> paginator = PublishProcessor.create();
    private ProgressBar progressBar;
    private boolean loading = false;
    private int pageNumber = 1;
    private final int VISIBLE_THRESHOLD = 1;
    private int lastVisibleItem, totalItemCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // removed for brevity...

        paginationAdapter = new PaginationAdapter();
        recyclerView.setAdapter(paginationAdapter);
        setUpLoadMoreListener();
        subscribeForData();
    }

    /**
     * setting listener to get callback for load more
     */
    private void setUpLoadMoreListener() {
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView,
                                   int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                totalItemCount = layoutManager.getItemCount();
                lastVisibleItem = layoutManager
                        .findLastVisibleItemPosition();
                if (!loading
                        && totalItemCount <= (lastVisibleItem + VISIBLE_THRESHOLD)) {
                    pageNumber++;
                    paginator.onNext(pageNumber);
                    loading = true;
                }
            }
        });
    }

    /**
     * subscribing for data
     */
    private void subscribeForData() {

        Disposable disposable = paginator
                .onBackpressureDrop()
                .doOnNext(page -> {
                    loading = true;
                    progressBar.setVisibility(View.VISIBLE);
                })
                .concatMapSingle(page -> dataFromNetwork(page)
                        .subscribeOn(Schedulers.io())
                        .doOnError(throwable -> {
                            // handle error
                        })
                            // continue emission in case of error also
                        .onErrorReturn(throwable -> new ArrayList<>()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(items -> {
                    paginationAdapter.addItems(items);
                    paginationAdapter.notifyDataSetChanged();
                    loading = false;
                    progressBar.setVisibility(View.INVISIBLE);
                });

        compositeDisposable.add(disposable);

        paginator.onNext(pageNumber);

    }

    //removed for brevity...
}

Now, you are all set to build and run the sample app which will load the data using the pagination implementation.

This is how we should implement the pagination in RecyclerView using the operators in RxJava.

For a complete working example, check out the project: RxJava2-Android-Samples

Master Kotlin Coroutines from here: Mastering Kotlin Coroutines

That's it for now.

Thanks

Amit Shekhar

You can connect with me on:

Read all of my high-quality blogs here.