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... | |
} | |
}); | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Thanks |
This comment has been minimized.
This comment has been minimized.
private int visibleThreshold = 5; means the number of items remain to the recycler before reaching the end? |
This comment has been minimized.
This comment has been minimized.
@panagorn I added comments. You can find details here. |
This comment has been minimized.
This comment has been minimized.
Nice, i was looking for something like this. Thank you. |
This comment has been minimized.
This comment has been minimized.
You should add a reset method:
And call it on onResume() in SampleActivity:
Because after you pause the activity and resume again, the condition |
This comment has been minimized.
This comment has been minimized.
totalItemCount = mLinearLayoutManager.getItemCount(); which gets the fixed count the first loading, may be wrong, I think should be adater.getItemCount(), get from adapter. |
This comment has been minimized.
This comment has been minimized.
Great, this is a very clean solution. Thank you. |
This comment has been minimized.
This comment has been minimized.
How can i add progress bar at the footer, while loading data ? |
This comment has been minimized.
This comment has been minimized.
I created a Helper class to get position for any LayoutManager based on Android implementation in LinearLayoutManager. https://gist.github.com/mipreamble/b6d4b3d65b0b4775a22e#file-recyclerviewpositionhelper-java |
This comment has been minimized.
This comment has been minimized.
@mipreamble |
This comment has been minimized.
This comment has been minimized.
How can i add a loading spinner as last item while i am fetching the data? |
This comment has been minimized.
This comment has been minimized.
My list of item is retrieved via JSON, how do I know what has been clicked?
Kindly advice >.< |
This comment has been minimized.
This comment has been minimized.
any updates on adding loading spinner while fetching data? |
This comment has been minimized.
This comment has been minimized.
@cielantropi, you can use an <android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout> public class MainActivity implements LoaderManager.LoaderCallbacks<List<...>>, SwipeRefreshLayout.OnRefreshListener {
private ...Adapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adapter = new ...Adapter(...);
getSupportLoaderManager().initLoader(1, null, this);
setContentView(R.layout.activity_main);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
swipeRefreshLayout.setOnRefreshListener(this);
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
recyclerView.setAdapter(adapter);
}
@Override
protected void onDestroy() {
getSupportLoaderManager().destroyLoader(1);
super.onDestroy();
}
@Override
public void onRefresh() {
if (!getSupportLoaderManager().hasRunningLoaders()) {
getSupportLoaderManager().restartLoader(1, null, this);
}
}
@Override
public Loader<List<FilterableAdapter.Item>> onCreateLoader(int id, Bundle args) {
if (swipeRefreshLayout != null && !swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(true); // This show the spinner on top of activity
}
return new OverviewLoader(this);
}
@Override
public void onLoadFinished(Loader<List<...>> loader, List<...> data) {
if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false); // This hides the spinner
}
adapter.addAll(data); // Custom method on adapter, of course
}
@Override
public void onLoaderReset(Loader<List<...>> loader) {
}
} |
This comment has been minimized.
This comment has been minimized.
if (layoutManager.findLastCompletelyVisibleItemPosition() == recyclerAdapter.getItemCount() - 1) { |
This comment has been minimized.
This comment has been minimized.
My Scroller always come on top on load more why? it should be smooth scroll from last position. |
This comment has been minimized.
This comment has been minimized.
@pushpendra-spartan: you could try out this https://github.com/rockerhieu/rv-adapter-endless/ |
This comment has been minimized.
This comment has been minimized.
RecyclerView.setOnScrollListener(listener) is deprecated now. Please update it to addOnScrollListener(new listener); |
This comment has been minimized.
This comment has been minimized.
@pushpendra-spartan u found any solution ? i also look at the code and did not find code for recyclerview selection position ? |
This comment has been minimized.
This comment has been minimized.
Implemented this together w/ List/Endless List Adapter w/ code sample. |
This comment has been minimized.
This comment has been minimized.
This doesn't seem to work well in a fragment - since during paging (in a viewpager for multiple lists) or rotation the recyclerview adapter retains all its items, but the listener must get recreated onCreateView (meaning it thinks its loading and on page 1). I worked around it by making a no parameter constructor (that I can call in onCreate or I can make the listener as a member variable) and then a setLayoutManager (that I can call in onCreateView) but it feels like a hack. |
This comment has been minimized.
This comment has been minimized.
@jt-gilkeson i'm in the same situation as you. Just found out another solution. You don't have to call Just use mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener((LinearLayoutManager) mLayoutManager) {
@Override
public void onLoadMore(int current_page) {
// Do somthing with the list
// Notify the adapter that the data has changed
mAdapter.notifyDataSetChanged();
}
});
Working with recyclerview inside a viewpager fragment. |
This comment has been minimized.
This comment has been minimized.
How do you handle rotation changes? some pages are reloading on rotation changes and duplicate entries are shown on the list. Did anyone faced this issue? |
This comment has been minimized.
This comment has been minimized.
@akuafi My situation seemed to be the same as yours. I ended up handling the page number in the activty / fragment itself rather than relying on the listener. Use savedinstancestate as appropriate. This way, you don't need to call mAdapter.notifyDatasetChanged() which is processor heavy if you have 1000+ records :) |
This comment has been minimized.
This comment has been minimized.
how do i add progress bar at the bottom and show when scroll at the end?? |
This comment has been minimized.
This comment has been minimized.
OnScrollListener is deprecated… we should use OnScrollChangedListener instead |
This comment has been minimized.
This comment has been minimized.
@LouisCAD |
This comment has been minimized.
This comment has been minimized.
How does this scale when data gets huge, like a very loooong list? |
This comment has been minimized.
This comment has been minimized.
If I use you file in my app project, should I include a license or declaration in some place? |
This comment has been minimized.
This comment has been minimized.
Does this work with staggeredGridlLayoutManager?? |
This comment has been minimized.
This comment has been minimized.
https://gist.github.com/cyberrob/41a95de42498839d428b Add reset method and pageIndex to 1. |
This comment has been minimized.
This comment has been minimized.
change |
This comment has been minimized.
This comment has been minimized.
why the onScroll() method is getting called twice? Even its getting called initially. |
This comment has been minimized.
This comment has been minimized.
this is for my |
This comment has been minimized.
This comment has been minimized.
Thanks a lot for this, much appreciated. |
This comment has been minimized.
This comment has been minimized.
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; public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
}` 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. |
This comment has been minimized.
This comment has been minimized.
Will it work when stackFromEnd is set to true ? |
This comment has been minimized.
This comment has been minimized.
int lastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
int totalItem = mAdapter.getItemCount();
if (lastVisibleItem > totalItem - 2 && dy > 0 && notLoading) {
curPage++;
onLoadMore(curPage);
} |
This comment has been minimized.
This comment has been minimized.
@zfdang Why is there a synchronized block in |
This comment has been minimized.
This comment has been minimized.
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(); inside loadMore() ? that error comes, because i put these code inside loadMore() |
This comment has been minimized.
This comment has been minimized.
Thanks so much! excellent |
This comment has been minimized.
This comment has been minimized.
Will it work with orientation changes? |
This comment has been minimized.
This comment has been minimized.
Please note that using this view with a
If you call this in the |
This comment has been minimized.
This comment has been minimized.
@Dooskington your solutions works well for me. Thanks |
This comment has been minimized.
This comment has been minimized.
Its calling two times when |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
This thing is all the way bugged. |
This comment has been minimized.
This comment has been minimized.
if (layoutManager.findLastCompletelyVisibleItemPosition() == recyclerAdapter.getItemCount() - 1) { |
This comment has been minimized.
This comment has been minimized.
it works good with activity but I am facing problem with fragment onLoadMore not calling. how can be resolved this ? |
This comment has been minimized.
This comment has been minimized.
How to use it to scroll a "Horizontal" linearLayoutManager ? |
This comment has been minimized.
This comment has been minimized.
Cool. Useful ! |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
It works. Thanks! |
This comment has been minimized.
This comment has been minimized.
Great work! |
This comment has been minimized.
This comment has been minimized.
Thanks...Worked like a pro |
This comment has been minimized.
This comment has been minimized.
Thanks, you saved my time |
This comment has been minimized.
References
http://stackoverflow.com/a/26561717/500714
https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews