Skip to content

Instantly share code, notes, and snippets.

@ssinss
Last active January 19, 2024 08:52
Show Gist options
  • Save ssinss/e06f12ef66c51252563e to your computer and use it in GitHub Desktop.
Save ssinss/e06f12ef66c51252563e to your computer and use it in GitHub Desktop.
Endless RecyclerView OnScrollListener
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);
}
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public class SampleActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setOnScrollListener(new EndlessRecyclerOnScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int current_page) {
// do something...
}
});
}
}
@xuexin
Copy link

xuexin commented Feb 26, 2016

change if (totalItemCount > previousTotal) to if (totalItemCount != previousTotal) will be better for dataset changing.

@pritam-kadam
Copy link

why the onScroll() method is getting called twice? Even its getting called initially.

@esantiago1
Copy link

Copy link

ghost commented Mar 24, 2016

Thanks a lot for this, much appreciated.

@zfdang
Copy link

zfdang commented Mar 25, 2016

when I tried to use the above code, I found one problem: onLoadMore() might be called multiple times! So I have to make modification to this implementation to make sure only one onLoadMore is triggered. here are the changes:

`package com.zfdang.zsmth_android.listeners;

import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();

private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = false; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 2; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;

private int current_page = 1;

private LinearLayoutManager mLinearLayoutManager;

public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
    this.mLinearLayoutManager = linearLayoutManager;
}

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

    if(dy < 0) return;
    // check for scroll down
    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = mLinearLayoutManager.getItemCount();
    firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

    synchronized (this) {
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached, Do something
            current_page++;
            onLoadMore(current_page);
            loading = true;
        }
    }
}


public void setLoading(boolean loading) {
    this.loading = loading;
}

public abstract void onLoadMore(int current_page);

}`

when I'm using this listener, I will call listener.setLoading(false) explicitly to re-enable onLoadMore after loading is finished.

it works pretty well for me.

@chaitanya79
Copy link

Will it work when stackFromEnd is set to true ?

@aliang228
Copy link

                int lastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
                int totalItem = mAdapter.getItemCount();

                if (lastVisibleItem > totalItem - 2 && dy > 0 && notLoading) {
                    curPage++;
                    onLoadMore(curPage);
                }

@abhaysood
Copy link

@zfdang Why is there a synchronized block in onScrolled()?

@griajobag
Copy link

griajobag commented Aug 11, 2016

i faced an issue like this : "cannot call this method while RecyclerView is computing a layout or scrolling Load More". How should i do?

other questions, do i put

list.clear();
list.addAll(list);
adapter.notifySetDataChanged()

inside loadMore() ?

that error comes, because i put these code inside loadMore()

@vsalguero
Copy link

Thanks so much! excellent

@pratikpopat
Copy link

Will it work with orientation changes?

@Dooskington
Copy link

Please note that using this view with a SwipeRefreshLayout can cause some issues... You are going to have to reset the EndlessRecyclerViewScrollListener when you refresh, or else it gets confused and may not work when you continue to try to scroll again. There needs to be a basic reset() method added...

public void reset()
{
    previousTotalItemCount = 0;
    currentPage = 1;
}

If you call this in the onRefresh() of your SwipeRefreshLayout.OnRefreshListener(), it should work properly. It does for me.

@maxbax
Copy link

maxbax commented Oct 5, 2016

@Dooskington your solutions works well for me. Thanks

@pratikbutani
Copy link

pratikbutani commented Feb 9, 2017

Its calling two times when RecyclerView not have enough (2 or 3 row) data as height of screen.

@pratikbutani
Copy link

pratikbutani commented Feb 22, 2017

I have Updated this file as per my usage. I got problem like its calling two times when RecyclerView not have enough (2 or 3 row) data as height of screen.

@lectricas
Copy link

This thing is all the way bugged.
It stuck when I use SwipeOnRefresh even with proposet reset.
Eventually employed https://gist.github.com/nesquena/d09dc68ff07e845cc622 this

@gvsharma
Copy link

if (layoutManager.findLastCompletelyVisibleItemPosition() == recyclerAdapter.getItemCount() - 1) {
//load more
}

@sharad-appristine
Copy link

sharad-appristine commented Dec 13, 2017

it works good with activity but I am facing problem with fragment onLoadMore not calling.

how can be resolved this ?

@nahlanabil84
Copy link

How to use it to scroll a "Horizontal" linearLayoutManager ?

@ZaeemSattar
Copy link

Cool. Useful !
How to use it in horizontal Recyclerview ?

@Mukeshkrjha
Copy link

Not working when "visibleThreshold" set to 1 & current_page = 0;. Setting "visibleThreshold" as 2 and current_page = 0, cause an unnecessary "onLoadMore" call. Which is result in bad experience to user if there in not data on 2nd page.

@lahrm
Copy link

lahrm commented Mar 13, 2018

It works. Thanks!
It was not working for me because I was wrapping the recycler view in a scroll view in my layout file. I spent days to find that it was the root cause of the issue and that I should remove the scroll view.

@badrddinb
Copy link

Great work!
I'm using kotlin and i was stuck on this one.
Thank

@SakthivelFantain
Copy link

You should add a reset method:

public void reset(int previousTotal, boolean loading) {
  this.previousTotal = previousTotal;
  this.loading = loading;
}

And call it on onResume() in SampleActivity:

@Override
  public void onResume() {
    super.onResume();
    endlessScrollListener.reset(0, true);
}

Because after you pause the activity and resume again, the condition !loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold) is always false, thus onLoadMore is never called.

Thanks...Worked like a pro

@syukronhardianto
Copy link

change if (totalItemCount > previousTotal) to if (totalItemCount != previousTotal) will be better for dataset changing.

Thanks, you saved my time

@ayoubAnbara
Copy link

Thanks you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment