Skip to content

Instantly share code, notes, and snippets.

@talenguyen
Last active June 14, 2019 10:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save talenguyen/0a3d1e1f941a8a45adc2 to your computer and use it in GitHub Desktop.
Save talenguyen/0a3d1e1f941a8a45adc2 to your computer and use it in GitHub Desktop.
Implementation of Load More feature for RecyclerView
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public abstract class LoadMoreAdapter<T>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int VIEW_TYPE_LOAD_MORE = 1;
private List<T> items = new ArrayList<>();
private boolean enableLoadMore;
public void setEnableLoadMore(boolean enableLoadMore) {
if (this.enableLoadMore == enableLoadMore) {
return;
}
this.enableLoadMore = enableLoadMore;
notifyDataSetChanged();
}
public void setItems(List<T> items) {
this.items.clear();
addItems(items);
}
public void addItems(List<T> items) {
this.items.addAll(items);
notifyDataSetChanged();
}
public T getItem(int position) {
if (position < 0 || position > items.size() - 1) {
return null;
}
return items.get(position);
}
@Override public int getItemCount() {
return items == null ? 0 : items.size() + (enableLoadMore ? 1 : 0);
}
@Override public int getItemViewType(int position) {
if (enableLoadMore && position == getItemCount() - 1) {
return VIEW_TYPE_LOAD_MORE;
}
return super.getItemViewType(position);
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_LOAD_MORE) {
return onCreateLoadMoreViewHolder(parent);
}
return onCreateItemViewHolder(parent, viewType);
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!enableLoadMore || position != getItemCount() - 1) {
onBindItemViewHolder(holder, position);
}
}
public boolean isEnableLoadMore() {
return enableLoadMore;
}
public abstract RecyclerView.ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position);
public abstract RecyclerView.ViewHolder onCreateLoadMoreViewHolder(ViewGroup parent);
}
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public abstract class OnVerticalScrollListener extends RecyclerView.OnScrollListener {
private boolean lastItemVisible;
@Override public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!recyclerView.canScrollVertically(-1)) {
onScrolledToTop();
} else if (!recyclerView.canScrollVertically(1)) {
onScrolledToBottom();
} else if (dy < 0) {
onScrolledUp();
lastItemVisible = isLastItemVisible(recyclerView);
} else if (dy > 0) {
onScrolledDown();
boolean isLastItemVisible = isLastItemVisible(recyclerView);
if (lastItemVisible != isLastItemVisible) {
lastItemVisible = isLastItemVisible;
if (lastItemVisible) {
onScrolledDownToLastItem();
}
}
}
}
private boolean isLastItemVisible(RecyclerView recyclerView) {
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final int lastVisibleItemPosition =
((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
return lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1;
}
return false;
}
public void onScrolledDownToLastItem() {
}
public void onScrolledUp() {
}
public void onScrolledDown() {
}
public void onScrolledToTop() {
}
public void onScrolledToBottom() {
}
}
import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
public class SupportLoadMoreGridLayoutManager extends GridLayoutManager {
private SpanSizeLookupWrapper spanSizeLookupWrapper;
private LoadMoreAdapter adapter;
private Runnable computeLoadMore = new Runnable() {
@Override public void run() {
final int firstCompletelyVisibleItemPosition = findFirstCompletelyVisibleItemPosition();
final int lastCompletelyVisibleItemPosition = findLastCompletelyVisibleItemPosition();
if (firstCompletelyVisibleItemPosition < 0 || lastCompletelyVisibleItemPosition < 0) {
return;
}
final boolean allVisible = firstCompletelyVisibleItemPosition == 0
&& lastCompletelyVisibleItemPosition == adapter.getItemCount() - 1;
if (allVisible) {
// All visible then we don't need to show load more
spanSizeLookupWrapper.setLoadMorePosition(-1);
} else {
spanSizeLookupWrapper.setLoadMorePosition(adapter.getItemCount() - 1);
}
adapter.setEnableLoadMore(!allVisible);
}
};;
public SupportLoadMoreGridLayoutManager(Context context, int spanCount) {
this(context, spanCount, VERTICAL, false);
}
public SupportLoadMoreGridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
spanSizeLookupWrapper = new SpanSizeLookupWrapper(spanCount);
super.setSpanSizeLookup(spanSizeLookupWrapper);
}
@Override public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
spanSizeLookupWrapper.setPlugIn(spanSizeLookup);
}
@Override public void onItemsChanged(final RecyclerView recyclerView) {
super.onItemsChanged(recyclerView);
final RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (!(adapter instanceof LoadMoreAdapter)) {
throw new IllegalArgumentException("adapter must be instance of LoadMoreAdapter");
}
this.adapter = (LoadMoreAdapter) adapter;
// Remove the old to keep only one callback execute.
recyclerView.removeCallbacks(computeLoadMore);
recyclerView.post(computeLoadMore);
}
static class SpanSizeLookupWrapper extends SpanSizeLookup {
private final int spanCount;
private SpanSizeLookup plugIn;
private int loadMorePosition = -1;
public SpanSizeLookupWrapper(int spanCount) {
this.spanCount = spanCount;
}
public void setLoadMorePosition(int loadMorePosition) {
this.loadMorePosition = loadMorePosition;
}
public void setPlugIn(SpanSizeLookup plugIn) {
this.plugIn = plugIn;
}
@Override public void invalidateSpanIndexCache() {
super.invalidateSpanIndexCache();
loadMorePosition = -1;
}
@Override public int getSpanSize(int position) {
final int spanSize;
if (loadMorePosition == position) {
spanSize = this.spanCount;
return spanSize;
} else if (plugIn != null) {
spanSize = plugIn.getSpanSize(position);
} else {
spanSize = 1;
}
return spanSize;
}
}
}
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public class SupportLoadMoreLinearLayoutManager extends LinearLayoutManager {
private LoadMoreAdapter adapter;
private Runnable computeLoadMore = new Runnable() {
@Override public void run() {
final int firstCompletelyVisibleItemPosition = findFirstCompletelyVisibleItemPosition();
final int lastCompletelyVisibleItemPosition = findLastCompletelyVisibleItemPosition();
if (firstCompletelyVisibleItemPosition < 0 || lastCompletelyVisibleItemPosition < 0) {
return;
}
final boolean allVisible = firstCompletelyVisibleItemPosition == 0
&& lastCompletelyVisibleItemPosition == adapter.getItemCount() - 1;
adapter.setEnableLoadMore(!allVisible);
}
};
public SupportLoadMoreLinearLayoutManager(Context context, int orientation,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override public void onItemsChanged(final RecyclerView recyclerView) {
super.onItemsChanged(recyclerView);
final RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (!(adapter instanceof LoadMoreAdapter)) {
throw new IllegalArgumentException("adapter must be instance of LoadMoreAdapter");
}
this.adapter = (LoadMoreAdapter) adapter;
// Remove the old to keep only one callback execute.
recyclerView.removeCallbacks(computeLoadMore);
recyclerView.post(computeLoadMore);
}
}
@talenguyen
Copy link
Author

  1. Implement Adapter by extend from LoadMoreAdapter
  2. Setup LayoutManager use SupportLoadMoreGridLayoutManager or SupportLoadMoreLinearLayoutManager
  3. Add scroll listener use OnVerticalScrollListener like below then use are good to go.
recyclerView.addOnScrollListener(new OnVerticalScrollListener() {
            @Override public void onScrolledDownToLastItem() {
                super.onScrolledDownToLastItem();
                if (adapter.isEnableLoadMore()) {
                    onLoadMore();
                }
            }
        });

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