blog

Home / DeveloperSection / Blogs / Endless Recycler view with loader

Endless Recycler view with loader

Prateek sharma 3696 07-Apr-2018

EndlessRecylcer view in android 

Endless scrolling in android works similar to paging in web apps. Here it works just by scrolling upward. This class EndlessRecyclerViewScrollListener can be called from any class where you have implemented your recycler view.
The below code will automatically increment the page and makes the request to the server with a new page number.
scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {
                @Override
                public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
                    page = totalItemsCount / 10;
                    page = page + 1;
                    loadMoreData(page);
                }
            };

EndlessRecyclerViewScrollListener is the main class which performs the scrolling behavior of the application.
public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    // The current offset index of data you have loaded
    private int currentPage = 0;
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // Sets the starting page index
    private int startingPageIndex = 0;
    RecyclerView.LayoutManager mLayoutManager;
    public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
    }
    public EndlessRecyclerViewScrollListener(GridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }
    public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }
    public int getLastVisibleItem(int[] lastVisibleItemPositions) {
        int maxSize = 0;
        for (int i = 0; i < lastVisibleItemPositions.length; i++) {
            if (i == 0) {
                maxSize = lastVisibleItemPositions[i];
            }
            else if (lastVisibleItemPositions[i] > maxSize) {
                maxSize = lastVisibleItemPositions[i];
            }
        }
        return maxSize;
    }
    // This happens many times a second during a scroll, so be wary of the code you place here.
    // We are given a few useful parameters to help us work out if we need to load some more data,
    // but first we check if we are waiting for the previous load to finish.
    @Override
    public void onScrolled(RecyclerView view, int dx, int dy) {
        int lastVisibleItemPosition = 0;
        int totalItemCount = mLayoutManager.getItemCount();
        if (mLayoutManager instanceof StaggeredGridLayoutManager) {
            int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
            // get maximum element within the list
            lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
        } else if (mLayoutManager instanceof GridLayoutManager) {
            lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        } else if (mLayoutManager instanceof LinearLayoutManager) {
            lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        }
        // If the total item count is zero and the previous isn't, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount) {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0) {
                this.loading = true;
            }
        }
        // If it’s still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount)) {
            loading = false;
            previousTotalItemCount = totalItemCount;
        }
        // If it isn’t currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        // threshold should reflect how many total columns there are too
        if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
            currentPage++;
            onLoadMore(currentPage, totalItemCount, view);
            loading = true;
        }
    }
    // Call this method whenever performing new searches
    public void resetState() {
        this.currentPage = this.startingPageIndex;
        this.previousTotalItemCount = 0;
        this.loading = true;
    }
    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);
}
When you will receive a response with a new list you need to append the new list with the previous list.
So for that purpose, you can use the following code. First, it checks if the previous list is empty or not, if not it appends the new list with the previous list and if the original list is empty it passes it as a new list.
After the list is passed to the adapter you need to register the recycler view with the scroll listener.

Type collectionType = new TypeToken<Collection<TagMaster>>() {}.getType();                
if(questionSortModelList.size()<=0) {
    questionSortModelList = gson.fromJson(modelList, collectionType);
    mAdapter = new TagFragmentAdapter(getContext(), questionSortModelList);
    mAdapter.setLoaded(true);
    mRecyclerView.setAdapter(mAdapter);
    progressBar.setVisibility(View.GONE);
    }        
else {
    List<TagMaster> questionData= gson.fromJson(modelList, collectionType);
    questionSortModelList.addAll(questionData);
    int size = mAdapter.getItemCount();
    mAdapter.notifyItemRangeInserted(size, questionSortModelList.size() - 1);
    mAdapter.setLoaded(true);
    // mAdapter = new RecentActivityAdapter(getContext(), questionSortModelList);
    }
    mRecyclerView.addOnScrollListener(scrollListener);


To the same recycler view, you can add a loader at the bottom of the page by adding the following line of code.

You just need to update your adapter for a loader to appear. First, you need to create a layout file for a loader.

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

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal">
    <ProgressBar
        android:id="@+id/endless_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:progress="25" />
</RelativeLayout>


Update the adapter. For this purpose, you just need to add a view holder for the progress bar layout in your adapter and update the onCreateViewHolder and onBindViewHolder of the adapter.

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


    private List<TagMaster> tagMasterList;
    Context mContext;
    protected boolean showLoader = false;
    private static final int VIEWTYPE_ITEM = 0;
    private static final int VIEWTYPE_LOADER = 1;
    long loggedUserId = 0;
    public TagFragmentAdapter(Context context, List<TagMaster> tagMasters){
        mContext = context;
        tagMasterList = tagMasters;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder viewHolder = null;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        switch (viewType){
            case VIEWTYPE_ITEM:
                viewHolder = getViewHolder(parent, inflater);
                break;
            case VIEWTYPE_LOADER:
                View view = inflater.inflate(R.layout.progress_bar_layout, parent, false);
                viewHolder = new LoaderViewHolder(view);
                break;
        }
        return viewHolder;
    }

    @NonNull
    private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
        RecyclerView.ViewHolder viewHolder;
        View v1 = inflater.inflate(R.layout.tag_fragment_view, parent, false);
        viewHolder = new TagViewHolder(v1);
        return viewHolder;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        switch (getItemViewType(position)) {
            case VIEWTYPE_ITEM:
                final TagViewHolder holder = (TagViewHolder) viewHolder;
                final TagMaster tagMaster = tagMasterList.get(position);
                loggedUserId = Long.valueOf(SaveSharedPreference.getPrefUserId(mContext));
                holder.tagName.setText(tagMaster.getName());
                break;
            case VIEWTYPE_LOADER:
                LoaderViewHolder holder1 = (LoaderViewHolder) viewHolder;
                if (showLoader && tagMasterList.size() != 0){
                   holder1.mProgressBar.setVisibility(View.VISIBLE);
                }else {
                    holder1.mProgressBar.setVisibility(View.GONE);
                }
                break;
        }
    }
   public void setLoaded(Boolean loader) {
        showLoader = loader;
    }

    @Override
    public int getItemViewType(int position) {
        return (position == tagMasterList.size() - 1 && showLoader) ? VIEWTYPE_LOADER : VIEWTYPE_ITEM;
    }

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

    public class TagViewHolder extends RecyclerView.ViewHolder{
        //public TextView catergory;
        private TextView tagName, quesTags;
        private View mView;
        private ImageButton favButton;
        public TagViewHolder(View view){
            super(view);
            tagName = (TextView) view.findViewById(R.id.tag_view);
            quesTags = (TextView) view.findViewById(R.id.que_tags);
            favButton = (ImageButton) view.findViewById(R.id.fav_tags);
            mView = view;
        }
    }
    public class LoaderViewHolder extends RecyclerView.ViewHolder {
        ProgressBar mProgressBar;
        public LoaderViewHolder(View itemView) {
            super(itemView);
            mProgressBar = (ProgressBar) itemView.findViewById(R.id.endless_progress);
        }
    }
}


Updated 07-Apr-2018

Leave Comment

Comments

Liked By