Created
August 27, 2015 00:30
-
-
Save shayousefi/93e24324419966ff9ae3 to your computer and use it in GitHub Desktop.
Adding endless scroll support to a RecyclerView.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<item name="endless_scroll_support" type="id" /> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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