2

I haven't found yet the proper examples of how to make an app load more items from Retrofit onResponse while scrolling down the RecyclerView.

Here is how I'm loading my fist 20 items:

List<ModelPartners> model = new ArrayList<>();

Call<ResponsePartners> call = ApiClient.getRetrofit().getPartnerList(params);
call.enqueue(this);

My RecyclerView

PartnersAdapter adapter = new PartnersAdapter(getContext(), recyclerView, model);
recyclerView.setAdapter(adapter);

And here is my Retrofit onResponse:

@Override
    public void onResponse(Call<ResponsePartners> call, Response<ResponsePartners> response) {
        if (getActivity() != null && response.isSuccessful()) {
            List<ModelPartners> body = response.body().getData();
            //Rest of the code to add body to my Adapter and notifyDataSetChanged
            }
        }

My Problem is, every time I add the method to load more items on scroll, onResponse keeps loading non-stop till the last page. Even if I have put set loading to false.

Can someone please show how to properly use pagination in Retrofit? What libraries you used with Retrofit or show me how you did it your way? Thank you in advance

Alimov Shohrukh
  • 55
  • 3
  • 15
  • use google's [paging](https://developer.android.com/topic/libraries/architecture/paging.html) support library – pskink Aug 13 '18 at 12:19

2 Answers2

9

You need three things to achieve this, you need:

  1. You need an onscroll listener for the recycler view.
  2. You need a method to add new items to the recycler adapter
  3. You need to call your paginated endpoint

Assuming you have a model like this from the server

public class PagedList<T> {

    private int page = 0;
    private List<T> results = new ArrayList<>();
    private int totalResults = 0;
    private int totalPages = 0;

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public List<T> getResults() {
        return results;
    }

    public void setResults(List<T> results) {
        this.results = results;
    }

    public int getTotalResults() {
        return totalResults;
    }

    public void setTotalResults(int totalResults) {
        this.totalResults = totalResults;
    }

    public int getTotalPages() {
        return totalPages;
    }

    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }
}

Your OnScroll Listener: - In your activity this is what you would have, so add the second method to your activity

public void onCreate(Bundle savedInstance) {

    // setup your view components
    // ...

    // get the layout manager
    LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

    // rest endpoint
    apiEndpoint = retrofit.create(RetrofitEndpoint.class);

    // initialise loading state
    mIsLoading = false;
    mIsLastPage = false;

    // amount of items you want to load per page
    final int pageSize = 20;

    // set up scroll listener
    recyclerView.addOnScrollListener(new  RecyclerView.OnScrollListener() {
            @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            // number of visible items
            int visibleItemCount = layoutManager.getChildCount();
            // number of items in layout
            int totalItemCount = layoutManager.getItemCount();
            // the position of first visible item
            int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();

            boolean isNotLoadingAndNotLastPage = !mIsLoading && !mIsLastPage;
            // flag if number of visible items is at the last
            boolean isAtLastItem = firstVisibleItemPosition + visibleItemCount >= totalItemCount;
            // validate non negative values
            boolean isValidFirstItem = firstVisibleItemPosition >= 0;
            // validate total items are more than possible visible items
            boolean totalIsMoreThanVisible = totalItemCount >= pageSize;
            // flag to know whether to load more
            boolean shouldLoadMore = isValidFirstItem && isAtLastItem && totalIsMoreThanVisible && isNotLoadingAndNotLastPage

            if (shouldLoadMore) loadMoreItems(false);
        }
    });

    // load the first page
    loadMoreItems(true);
}

private void loadMoreItems(boolean isFirstPage) {
    // change loading state
    mIsLoading = true;
    mCurrentPage = mCurrentPage + 1;

    // update recycler adapter with next page
    apiEndpoint.getPagedList(mCurrentPage).enqueue(new Callback<PagedList<Object>>() {
        @Override
        public void onResponse(Call<PagedList<Object>> call, Response<PagedList<Object>> response) {
            PagedList<Object> result = response.body();

            if (result == null) return;
            else if (!isFirstPage) mAdapter.addAll(result.getResults());
            else mAdapter.setList(result.getResults());

            mIsLoading = false;
            mIsLastPage = mCurrentPage == result.getTotalPages();
        }

        @Override
        public void onFailure(Call<PagedList<Object>> call, Throwable t) {
            Log.e("SomeActivity", t.getMessage());
        }
    });
}

Recycler Adapter: - For the recycler adapter all you need is to add a method that adds items to its list, as below

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

    private List<Object> list;


    // .. other overridden members

    public void setList(List<Object> list) {
        this.list = list;
        notifyDataSetChanged();
    }

    public void addAll(List<Object> newList) {
        int lastIndex = list.size() - 1;
        list.addAll(newList);
        notifyItemRangeInserted(lastIndex, newList.size());
    }
}

Then finally your retrofit interface that takes the page, as below:

interface RetrofitEndpoint {

    @GET("paged/list/endpoint")
    Call<PagedList<Object>> getPagedList(@Query("page") int page);
}

Hope that helps.

Ebi Igweze
  • 390
  • 2
  • 7
3

@Alimov Shohrukh, I also tried for many ways for Pagination

1) Check this link,This is one way

2) I mention it step by step

from API side - you need to pass pageConunt, pageIndex and get data based on above parameters

pageConunt - means how many data you want get from server for i.e - 10 pageIndex - page number for i.e 1,2,3 etc

public class DemoActivity extends BaseActivity{

List<BaseModel> orderList = new ArrayList<>();

    ListAdapter listAdapter;
    LinearLayoutManager layoutManager;
OrderListAdapter orderListAdapter;

    int pageIndex = 1; // you can pass 1,2,3...
    int pageCount = 10; //you can pass 10,20...


    boolean isApiSuccess = false;
    //if api get success == true then you need to work with load more sp we take one boolean variable

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

        findView();
        setRecyclerViewPagination();

    }

    private void findView() {
         //recyclerview
        rv_po_order_number = findViewById(R.id.rv_po_order_number);

    }


    //custom OnScrollListener
private RecyclerView.OnScrollListener recyclerViewOnScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

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

            if (isApiSuccess) {
                pageIndex++;
                listTestApiCall(pageIndex, searchQuery);
                Log.e(TAG, pageIndex + " page count ");

                orderListAdapter.showLoading(true);

                rv_po_order_number.post(new Runnable() {
                    public void run() {
                        // There is no need to use notifyDataSetChanged()
                        orderListAdapter.notifyDataSetChanged();
                    }
                });
            }
        }
    };
private void setRecyclerViewPagination() {
        orderList = new ArrayList<>();
        orderListAdapter = new OrderListAdapter(mActivity, orderList);

        layoutManager = new LinearLayoutManager(mActivity, LinearLayoutManager.VERTICAL, false);
        rv_po_order_number.setLayoutManager(layoutManager);
        rv_po_order_number.setHasFixedSize(true);

        rv_po_order_number.setAdapter(orderListAdapter);
        rv_po_order_number.addOnScrollListener(recyclerViewOnScrollListener);

        listTestApiCall(pageCount,pageIndex);

    }

private void listTestApiCall(final int pageCount,final int pageIndex) {

        //  if (CommonUtils.isConnectingToInternet(mActivity)) {
        if (validateInternetConn(mActivity)) {//check internet

            final boolean isFirstPage = pageIndex == 1 ? true : false;

            if (isFirstPage)
                //do your stuff - if you want to show loader

            Call<BaseModel> call = ApiClient.getClient().listApiCall(pageCount, pageIndex);

            call.enqueue(new Callback<BaseModel>() {
                @Override
                public void onResponse(Call<BaseModel> call, Response<BaseModel> response) {
                    try {
                        if (response.isSuccessful()) {

                            boolean success = response.body().getStatus();
                            isApiSuccess = response.body().getStatus();

                            if (success) {

                                List<BaseModel> list = new ArrayList<>();

                                if (response.body().getData() != null) {
                                    list = response.body().getData();

                                    if (isFirstPage)
                                        orderList.clear();

                                    orderList.addAll(response.body().getData());

                                }

                                if (list != null && list.size() > 0) {
                                    isApiSuccess = true;
                                } else {
                                    isApiSuccess = false;
                                }

                            } else if (!success) {
                                isApiSuccess = false;
                                String message = response.body().getMessage();

                            }


                            listAdapter.showLoading(false);
                            listAdapter.notifyDataSetChanged();
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                        Log.e("Tag", "error=" + e.toString());
                        isApiSuccess = false;
                    }


                }

                @Override
                public void onFailure(Call<BaseModel> call, Throwable t) {
                    Log.e("Tag", "error" + t.toString());

                    isApiSuccess = false;
                                    }
            });
        }
  }
   }

here is OrderListAdapter

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


   Activity mActivity;

   List<OrderListBaseModel.DataBean> mList;


   PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick;
   DeleteUpdatePOOnClick deleteUpdatePOOnClick;

   public static final int TYPE_FOOTER = 1;
   public static final int TYPE_ITEM = 2;

   protected boolean showLoader = true;

   public OrderListAdapter(Activity mActivity, List<OrderListBaseModel.DataBean> mList, PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick,DeleteUpdatePOOnClick deleteUpdatePOOnClick) {
       this.mActivity = mActivity;
       this.purchaseOrderReceiveOnClick = purchaseOrderReceiveOnClick;
       this.deleteUpdatePOOnClick = deleteUpdatePOOnClick;
       this.mList = mList;


   }

   public OrderListAdapter(Activity mActivity, List<OrderListBaseModel.DataBean> mList) {
       this.mActivity = mActivity;

       this.mList = mList;



   }


   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       if (viewType == TYPE_ITEM) {
           View v = LayoutInflater.from(parent.getContext())
                   .inflate(R.layout.row_purchase_order_receive, parent, false);
           return new DataViewHolder(v, purchaseOrderReceiveOnClick);
       } else if (viewType == TYPE_FOOTER) {
           View v = LayoutInflater.from(parent.getContext())
                   .inflate(R.layout.loading_layout, parent, false);
           return new FooterViewHolder(v);
       } else
           return null;
   }

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


       if (holder instanceof DataViewHolder) {

           final DataViewHolder dataViewHolder = (DataViewHolder) holder;


           final OrderListBaseModel.DataBean dataBean = getItem(position);
           if (dataBean != null) {
               dataViewHolder.setPosition(position);
                dataViewHolder.setSingleBean(dataBean);


           }


       }
   }

   private OrderListBaseModel.DataBean getItem(int position) {
       if (mList.size() > 0)
           return mList.get(position);

       else
           return null;
   }

   public void showLoading(boolean status) {
       showLoader = status;
   }


   @Override
   public int getItemViewType(int position) {
       if ((position == mList.size() - 1) && showLoader) {
           return TYPE_FOOTER;
       }
       return TYPE_ITEM;
   }
   public void removeItem(int position) {
       mList.remove(position);
       notifyItemRemoved(position);
   }
   @Override
   public int getItemCount() {
       return mList.size();
   }


   public static class DataViewHolder extends RecyclerView.ViewHolder {
       int position;
       OrderListBaseModel.DataBean dataBean;
       private TextView tv_row_po_no,textViewOptions;

       public DataViewHolder(View v, final PurchaseOrderReceiveOnClick purchaseOrderReceiveOnClick) {
           super(v);
           tv_row_po_no = v.findViewById(R.id.tv_row_po_no);
           textViewOptions = v.findViewById(R.id.textViewOptions);



       }

       public void setPosition(int position) {
           this.position = position;
       }

       public void setSingleBean(OrderListBaseModel.DataBean dataBean) {
           this.dataBean = dataBean;
       }
   }

   private class FooterViewHolder extends RecyclerView.ViewHolder {

       public FooterViewHolder(View view) {
           super(view);

       }
   }


   }

here is xml code

loading_layout.xml

<?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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/linearRoot"
    android:gravity="center_horizontal"
    android:layout_marginTop="@dimen/_5sdp">
<com.app.trackpoint.custom_loader.MYLoader
        android:layout_width="@dimen/_30sdp"
        app:my_color="@color/colorAccent"
        android:layout_height="@dimen/_30sdp" />
</LinearLayout>
Adil
  • 749
  • 1
  • 8
  • 27