Skip to content

Instantly share code, notes, and snippets.

@neevek
Last active May 27, 2017 14:55
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 neevek/b774c433072500f70c2c23e855f4c037 to your computer and use it in GitHub Desktop.
Save neevek/b774c433072500f70c2c23e855f4c037 to your computer and use it in GitHub Desktop.
A RecyclerView.Adapter implementation acts as a wrapper of another RecyclerView.Adapter to support "HeaderView" and "FooterView" in a non-intrusive way.
/**
* Inspired by Qisen Tang, ref: http://www.woaitqs.cc/android/2017/04/11/new-way-to-add-header-and-footer.html
*
* <pre>
* <code>
* Usage:
*
* RecyclerViewAdapterWrapper adapterWrapper =
* RecyclerViewAdapterWrapper.wrap(new MyRecyclerViewAdapter())
* .headerView(headerView)
* .footerView(footerView)
* .setHeaderViewVisible(false)
* .setFooterViewVisible(false);
*
* recyclerView.setAdapter(adapterWrapper);
* // at later time, show the header or footer view accordingly
* adapterWrapper.setFooterViewVisible(true);
* </code>
* </pre>
*
* Author: neevek <i@neevek.net>
* Date: 2017-05-23
*/
public class RecyclerViewAdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Handler mMainHandler = new Handler(Looper.getMainLooper());
private RecyclerView.Adapter<RecyclerView.ViewHolder> mWrappedAdapter;
private View mHeaderView;
private View mFooterView;
private final static int VIEW_TYPE_HEADER = 1;
private final static int VIEW_TYPE_FOOTER = 2;
private final static int VIEW_TYPE_OFFSET = 3;
@SuppressWarnings("unchecked")
private RecyclerViewAdapterWrapper(RecyclerView.Adapter<?> wrappedAdapter) {
mWrappedAdapter = (RecyclerView.Adapter<RecyclerView.ViewHolder>)wrappedAdapter;
}
public static RecyclerViewAdapterWrapper wrap(RecyclerView.Adapter<?> wrappedAdapter) {
return new RecyclerViewAdapterWrapper(wrappedAdapter);
}
public RecyclerViewAdapterWrapper headerView(View headerView) {
mHeaderView = headerView;
return this;
}
public RecyclerViewAdapterWrapper footerView(View footerView) {
mFooterView = footerView;
return this;
}
public RecyclerViewAdapterWrapper setHeaderViewVisible(boolean visible) {
if (mHeaderView == null ||
(mHeaderView.getVisibility() == View.VISIBLE) == visible) {
return this;
}
if (visible) {
mHeaderView.setVisibility(View.VISIBLE);
mMainHandler.post(this::notifyDataSetChanged);
} else {
mHeaderView.setVisibility(View.GONE);
mMainHandler.post(this::notifyDataSetChanged);
}
return this;
}
public RecyclerViewAdapterWrapper setFooterViewVisible(boolean visible) {
if (mFooterView == null ||
(mFooterView.getVisibility() == View.VISIBLE) == visible) {
return this;
}
if (visible) {
mFooterView.setVisibility(View.VISIBLE);
mMainHandler.post(this::notifyDataSetChanged);
} else {
mFooterView.setVisibility(View.GONE);
mMainHandler.post(this::notifyDataSetChanged);
}
return this;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_HEADER: return new HeaderFooterViewHolder(mHeaderView);
case VIEW_TYPE_FOOTER: return new HeaderFooterViewHolder(mFooterView);
}
return mWrappedAdapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (!isHeaderView(position) && !isFooterView(position)) {
mWrappedAdapter.onBindViewHolder(holder, recalculatePosition(position), payloads);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!isHeaderView(position) && !isFooterView(position)) {
mWrappedAdapter.onBindViewHolder(holder, recalculatePosition(position));
}
}
@Override
public int getItemViewType(int position) {
if (isHeaderView(position)) {
return VIEW_TYPE_HEADER;
}
if (isFooterView(position)) {
return VIEW_TYPE_FOOTER;
}
int itemType = mWrappedAdapter.getItemViewType(recalculatePosition(position));
if (itemType <= 0) {
// avoid stepping over the header view or footer view
itemType = (-itemType) + 1;
}
return itemType + VIEW_TYPE_OFFSET;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(hasStableIds);
mWrappedAdapter.setHasStableIds(hasStableIds);
}
@Override
public long getItemId(int position) {
if (isHeaderView(position)) {
return VIEW_TYPE_HEADER;
}
if (isFooterView(position)) {
return VIEW_TYPE_HEADER;
}
return mWrappedAdapter.getItemId(recalculatePosition(position));
}
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
if (!(holder instanceof HeaderFooterViewHolder)) {
mWrappedAdapter.onViewRecycled(holder);
}
}
@Override
public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
if (!(holder instanceof HeaderFooterViewHolder)) {
return mWrappedAdapter.onFailedToRecycleView(holder);
}
return super.onFailedToRecycleView(holder);
}
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
if (!(holder instanceof HeaderFooterViewHolder)) {
mWrappedAdapter.onViewAttachedToWindow(holder);
}
}
@Override
public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
if (!(holder instanceof HeaderFooterViewHolder)) {
mWrappedAdapter.onViewDetachedFromWindow(holder);
}
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
mWrappedAdapter.onAttachedToRecyclerView(recyclerView);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
mWrappedAdapter.onDetachedFromRecyclerView(recyclerView);
}
@Override
public int getItemCount() {
return mWrappedAdapter.getItemCount() +
(isHeaderViewVisible() ? 1 : 0) +
(isFooterViewVisible() ? 1 : 0);
}
private int recalculatePosition(int position) {
return isHeaderViewVisible() ? position - 1 : position;
}
private boolean isHeaderView(int position) {
return position == 0 && isHeaderViewVisible();
}
private boolean isFooterView(int position) {
return position == getItemCount() - 1 && isFooterViewVisible();
}
private boolean isHeaderViewVisible() {
return mHeaderView != null && mHeaderView.getVisibility() == View.VISIBLE;
}
private boolean isFooterViewVisible() {
return mFooterView != null && mFooterView.getVisibility() == View.VISIBLE;
}
private static class HeaderFooterViewHolder extends RecyclerView.ViewHolder {
private HeaderFooterViewHolder(View itemView) {
super(itemView);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment