Created
February 3, 2016 13:49
-
-
Save TheReprator/7c9007e0cf8cf49a773c to your computer and use it in GitHub Desktop.
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
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