Skip to content

Instantly share code, notes, and snippets.

@TheReprator
Created February 3, 2016 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheReprator/7c9007e0cf8cf49a773c to your computer and use it in GitHub Desktop.
Save TheReprator/7c9007e0cf8cf49a773c to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class Empty_EndlessRecyclerView extends RecyclerView {
private List<OnScrollListener> onScrollListeners = new ArrayList<>();
private EndlessScrollListener endlessScrollListener;
private AdapterWrapper adapterWrapper;
private View progressView;
private View mEmptyView;
private boolean refreshing;
private int threshold = 1;
private boolean mLazy = true;
private boolean mObserverAttached = false;
public Empty_EndlessRecyclerView(Context context) {
this(context, null);
}
public Empty_EndlessRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Empty_EndlessRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
super.setOnScrollListener(new OnScrollListenerImpl());
}
@Override
public Adapter getAdapter() {
return adapterWrapper.getAdapter();
}
@Override
public void setAdapter(Adapter adapter) {
//noinspection unchecked
adapterWrapper = new AdapterWrapper(adapter);
super.setAdapter(adapterWrapper);
}
/**
* Use {@link #addOnScrollListener(OnScrollListener)} and
* {@link #removeOnScrollListener(OnScrollListener)} methods instead. Calling this method will
* cause {@link UnsupportedOperationException}.
*/
@Override
public void setOnScrollListener(OnScrollListener listener) {
throw new UnsupportedOperationException("use addOnScrollListener(OnScrollListener) and " +
"removeOnScrollListener(OnScrollListener) instead");
}
@Override
public LinearLayoutManager getLayoutManager() {
return (LinearLayoutManager) super.getLayoutManager();
}
/**
* @param layout instances of {@link LinearLayoutManager} only
*/
@Override
public void setLayoutManager(LayoutManager layout) {
if (layout instanceof LinearLayoutManager) {
super.setLayoutManager(layout);
} else {
throw new IllegalArgumentException(
"layout manager must be an instance of LinearLayoutManager");
}
}
/**
* Adds {@link RecyclerView.OnScrollListener} to use with this view.
*
* @param listener listener to add
*/
public void addOnScrollListener(OnScrollListener listener) {
if (listener == null) {
throw new NullPointerException("listener is null");
}
onScrollListeners.add(listener);
}
/**
* Removes {@link RecyclerView.OnScrollListener} to use with this view.
*
* @param listener listener to remove
*/
public void removeOnScrollListener(OnScrollListener listener) {
if (listener == null) {
throw new NullPointerException("listener is null");
}
onScrollListeners.remove(listener);
}
public void setPager(ProcessedResult pager) {
if (pager != null) {
endlessScrollListener = new EndlessScrollListener(pager);
endlessScrollListener.setThreshold(threshold);
addOnScrollListener(endlessScrollListener);
} else if (endlessScrollListener != null) {
removeOnScrollListener(endlessScrollListener);
endlessScrollListener = null;
}
}
/**
* Sets progress view to show on the bottom of the list when loading starts.
*
* @param layoutResId layout resource ID
*/
public void setProgressView(int layoutResId) {
setProgressView(LayoutInflater
.from(getContext())
.inflate(layoutResId, this, false));
}
/**
* Sets progress view to show on the bottom of the list when loading starts.
*
* @param view the view
*/
public void setProgressView(View view) {
progressView = view;
}
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
}
public boolean isEager() {
return !mLazy;
}
private void updateEmptyStatus(boolean empty) {
if (empty) {
setVisibility(GONE);
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
}
} else {
setVisibility(VISIBLE);
if (mEmptyView != null) {
mEmptyView.setVisibility(GONE);
}
}
}
/**
* If async operation completed you may want to call this method to hide progress view.
*
* @param refreshing {@code true} if list is currently refreshing, {@code false} otherwise
*/
public void setRefreshing(boolean refreshing) {
if (this.refreshing == refreshing) {
return;
}
this.refreshing = refreshing;
this.adapterWrapper.notifyDataSetChanged();
}
private class OnScrollListenerImpl extends OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
for (OnScrollListener listener : onScrollListeners) {
listener.onScrolled(recyclerView, dx, dy);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
for (OnScrollListener listener : onScrollListeners) {
listener.onScrollStateChanged(recyclerView, newState);
}
}
}
private class EndlessScrollListener extends OnScrollListener {
private ProcessedResult pager;
private int threshold = 1;
public EndlessScrollListener(ProcessedResult pager) {
if (pager == null) {
throw new NullPointerException("pager is null");
}
this.pager = pager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy < 0)
return;
int lastVisibleItemPosition = getLayoutManager()
.findLastVisibleItemPosition();
int lastItemPosition = getAdapter().getItemCount();
if (!refreshing && lastItemPosition - lastVisibleItemPosition <= threshold)
{
setRefreshing(true);
pager.processedResult();
}
}
private void setThreshold(int threshold) {
if (threshold <= 0) {
throw new IllegalArgumentException("illegal threshold: " + threshold);
}
this.threshold = threshold;
}
}
private class AdapterWrapper extends Adapter<ViewHolder> {
private static final int PROGRESS_VIEW_TYPE = -1;
private Adapter<ViewHolder> adapter;
private ProgressViewHolder progressViewHolder;
private AdapterDataObserver mObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
updateEmptyStatus(isEmpty());
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
updateEmptyStatus(isEmpty());
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
updateEmptyStatus(isEmpty());
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
super.onItemRangeRemoved(positionStart, itemCount);
updateEmptyStatus(isEmpty());
}
};
public AdapterWrapper(Adapter<ViewHolder> adapter) {
if (adapter == null) {
throw new NullPointerException("adapter is null");
}
this.adapter = adapter;
setHasStableIds(adapter.hasStableIds());
}
@Override
public int getItemCount() {
return adapter.getItemCount() + (refreshing && progressView != null ? 1 : 0);
}
@Override
public long getItemId(int position) {
return position == adapter.getItemCount() ? NO_ID : adapter.getItemId(position);
}
@Override
public int getItemViewType(int position) {
return refreshing & position == adapter.getItemCount() ? PROGRESS_VIEW_TYPE :
adapter.getItemViewType(position);
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
adapter.onAttachedToRecyclerView(recyclerView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (position < adapter.getItemCount()) {
adapter.onBindViewHolder(holder, position);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return viewType == PROGRESS_VIEW_TYPE ? progressViewHolder = new ProgressViewHolder() :
adapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
adapter.onDetachedFromRecyclerView(recyclerView);
}
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
return holder == progressViewHolder || adapter.onFailedToRecycleView(holder);
}
@Override
public void onViewAttachedToWindow(ViewHolder holder) {
if (holder == progressViewHolder) {
return;
}
adapter.onViewAttachedToWindow(holder);
}
@Override
public void onViewDetachedFromWindow(ViewHolder holder) {
if (holder == progressViewHolder) {
return;
}
adapter.onViewDetachedFromWindow(holder);
}
@Override
public void onViewRecycled(ViewHolder holder) {
if (holder == progressViewHolder) {
return;
}
adapter.onViewRecycled(holder);
}
@Override
public void registerAdapterDataObserver(AdapterDataObserver observer) {
super.registerAdapterDataObserver(observer);
adapter.registerAdapterDataObserver(mObserver);
mObserverAttached = true;
if (isEager()) {
updateEmptyStatus(isEmpty());
}
}
@Override
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
super.unregisterAdapterDataObserver(observer);
Adapter adapter = getAdapter();
if (adapter != null && mObserverAttached) {
adapter.unregisterAdapterDataObserver(mObserver);
mObserverAttached = false;
}
}
public Adapter<ViewHolder> getAdapter() {
return adapter;
}
public boolean isEmpty() {
Adapter adapter = getAdapter();
return adapter == null ? true : adapter.getItemCount() == 0;
}
private class ProgressViewHolder extends ViewHolder {
public ProgressViewHolder() {
super(progressView);
}
}
}
public interface ProcessedResult {
void processedResult();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment