Last active
May 27, 2017 14:55
-
-
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.
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
/** | |
* 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