Skip to content

Instantly share code, notes, and snippets.

@shayousefi
Created August 27, 2015 00:30
Show Gist options
  • Save shayousefi/93e24324419966ff9ae3 to your computer and use it in GitHub Desktop.
Save shayousefi/93e24324419966ff9ae3 to your computer and use it in GitHub Desktop.
Adding endless scroll support to a RecyclerView.
/**
* A class that adds endless scroll support to a {@link RecyclerView}.
*/
public class EndlessScrollSupport {
private final RecyclerView recyclerView;
private EndlessScrollListener endlessScrollListener;
private int startPage = 0;
/** The current offset index of data you have loaded. */
private int currentPage = startPage;
/**
* The minimum amount of rows to have below your current scroll position
* before loading more.
*/
private int rowThreshold = 3;
/** The total number of items in the dataset after the last load. */
private int prevTotalItemCount = 0;
/** Whether we are still waiting for the last set of data to load. */
private boolean loading = true;
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (endlessScrollListener != null) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
int totalItemCount = layoutManager.getItemCount();
int lastVisibleItem = findLastVisibleItemPosition(layoutManager);
int visibleThreshold = findVisibleThreshold(layoutManager);
if (totalItemCount == 0 && prevTotalItemCount > totalItemCount) {
// Assume the list is invalidated, so reset back to initial state.
loading = true;
prevTotalItemCount = totalItemCount;
currentPage = startPage;
} else if (loading && prevTotalItemCount < totalItemCount) {
// Loading has finished.
loading = false;
prevTotalItemCount = totalItemCount;
currentPage++;
} else if (!loading && lastVisibleItem + visibleThreshold >= totalItemCount) {
// Fetch more data.
loading = true;
endlessScrollListener.onLoadMore(currentPage + 1, totalItemCount);
}
}
}
};
private EndlessScrollSupport(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
private int findLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
int lastVisibleItem = RecyclerView.NO_POSITION;
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
lastVisibleItem = manager.findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) layoutManager;
lastVisibleItem = PrimUtils.max(manager.findLastVisibleItemPositions(null));
}
return lastVisibleItem;
}
private int findVisibleThreshold(RecyclerView.LayoutManager layoutManager) {
int visibleThreshold = rowThreshold;
if (layoutManager instanceof GridLayoutManager) {
GridLayoutManager manager = (GridLayoutManager) layoutManager;
visibleThreshold = rowThreshold * manager.getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) layoutManager;
visibleThreshold = rowThreshold * manager.getSpanCount();
}
return visibleThreshold;
}
public static EndlessScrollSupport addTo(@NonNull RecyclerView view) {
EndlessScrollSupport support = (EndlessScrollSupport)
view.getTag(R.id.endless_scroll_support);
if (support == null) {
support = new EndlessScrollSupport(view);
support.attach(view);
} else {
Log.w("RecyclerView already has EndlessScrollSupport.");
}
return support;
}
private void attach(RecyclerView view) {
view.setTag(R.id.endless_scroll_support, this);
view.addOnScrollListener(scrollListener);
}
public static EndlessScrollSupport removeFrom(@NonNull RecyclerView view) {
EndlessScrollSupport support = (EndlessScrollSupport)
view.getTag(R.id.endless_scroll_support);
if (support != null) {
support.detach(view);
} else {
Log.w("RecyclerView does not have EndlessScrollSupport.");
}
return support;
}
private void detach(RecyclerView view) {
view.removeOnScrollListener(scrollListener);
view.setTag(R.id.endless_scroll_support, null);
endlessScrollListener = null;
}
public int getRowThreshold() {
return rowThreshold;
}
public EndlessScrollSupport setRowThreshold(int rowThreshold) {
this.rowThreshold = rowThreshold;
return this;
}
public int getStartPage() {
return startPage;
}
public EndlessScrollSupport setStartPage(int startPage) {
this.startPage = startPage;
return this;
}
public EndlessScrollListener getEndlessScrollListener() {
return endlessScrollListener;
}
public EndlessScrollSupport setEndlessScrollListener(EndlessScrollListener listener) {
endlessScrollListener = listener;
return this;
}
public interface EndlessScrollListener {
void onLoadMore(int page, int totalItemCount);
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="endless_scroll_support" type="id" />
</resources>
/**
* A utility class containing helper primitive methods.
*/
public final class PrimUtils {
private PrimUtils() {
throw new IllegalStateException("Non-instantiable utility class");
}
public static int max(int... ints) {
int max = Integer.MIN_VALUE;
for (int i : ints) {
max = i > max ? i : max;
}
return max;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment