定義:
裝飾設(shè)計(jì)模式也稱包裝設(shè)計(jì)模式,用來(lái)動(dòng)態(tài)的擴(kuò)展對(duì)象的功能,也是繼承關(guān)系的的一種替代方案之一。
說(shuō)個(gè)大白話就是,在不使用的繼承的方式下,采用裝飾設(shè)計(jì)模式可以擴(kuò)展一個(gè)對(duì)象的功能,可以使一個(gè)對(duì)象變得越來(lái)越強(qiáng)大。
我們首先看下效果圖

我們都知道listview是可以添加頭部和尾部的,我們大概看下,具體的可以自己去看下,源碼并不難。
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
//實(shí)際上呢,listview是將adapter添加了一個(gè)包裹類
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
//代碼省略
}
我們可以模仿listview進(jìn)行adapter的封裝
首先WrapRecyclerAdapter類是繼承于RecylerView的adapter,參數(shù)如下
private final static String TAG = "WrapRecyclerAdapter";
// 用來(lái)存放底部和頭部View的集合 比Map要高效一些
private SparseArray<View> mHeaderViews;
private SparseArray<View> mFooterViews;
// 基本的頭部類型開始位置 用于viewType
private static int BASE_ITEM_TYPE_HEADER = 10000000;
// 基本的底部類型開始位置 用于viewType
private static int BASE_ITEM_TYPE_FOOTER = 20000000;
// 列表的Adapter
private RecyclerView.Adapter mAdapter;
//通過(guò)構(gòu)造函數(shù)的方法
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaderViews = new SparseArray<>();
mFooterViews = new SparseArray<>();
}
我們首先定義四個(gè)方法,添加頭部,尾部,刪除頭部,尾部
/**
* 添加頭部
*/
public void addHeaderView(View view) {
int position = mHeaderViews.indexOfValue(view);
if (position < 0) {
mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
}
notifyDataSetChanged();
}
/**
* 添加底部
*/
public void addFooterView(View view) {
int position = mFooterViews.indexOfValue(view);
if (position < 0) {
mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
}
notifyDataSetChanged();
}
/**
* 移除頭部
*/
public void removeHeaderView(View view) {
int index = mHeaderViews.indexOfValue(view);
if (index < 0) return;
mHeaderViews.removeAt(index);
notifyDataSetChanged();
}
/**
* 移除底部
*/
public void removeFooterView(View view) {
int index = mFooterViews.indexOfValue(view);
if (index < 0) return;
mFooterViews.removeAt(index);
notifyDataSetChanged();
}
然后我們看下我們復(fù)寫的第一個(gè)方法 getItemCount()很簡(jiǎn)單: 底部條數(shù) + 頭部條數(shù) + Adapter的條數(shù)
@Override
public int getItemCount() {
// 條數(shù)三者相加 = 底部條數(shù) + 頭部條數(shù) + Adapter的條數(shù)
return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
}
然后看下我們復(fù)寫的第二個(gè)方法onCreateViewHolder()方法
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// viewType 可能就是 SparseArray 的key
if (isHeaderViewType(viewType)) {
View headerView = mHeaderViews.get(viewType);
return createHeaderFooterViewHolder(headerView);
}
if (isFooterViewType(viewType)) {
View footerView = mFooterViews.get(viewType);
return createHeaderFooterViewHolder(footerView);
}
return mAdapter.onCreateViewHolder(parent, viewType);
}
判斷是不是頭部或者底部類型并創(chuàng)建ViewHolder
/**
* 是不是底部類型
*/
private boolean isFooterViewType(int viewType) {
int position = mFooterViews.indexOfKey(viewType);
return position >= 0;
}
/**
* 創(chuàng)建頭部或者底部的ViewHolder
*/
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
/**
* 是不是頭部類型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaderViews.indexOfKey(viewType);
return position >= 0;
}
注意這里viewType不是position所以我們一定要復(fù)寫getItemViewType將返回的值賦值給viewType
@Override
public int getItemViewType(int position) {
if (isHeaderPosition(position)) {
// 直接返回position位置的key
return mHeaderViews.keyAt(position);
}
if (isFooterPosition(position)) {
// 直接返回position位置的key
position = position - mHeaderViews.size() - mAdapter.getItemCount();
return mFooterViews.keyAt(position);
}
// 返回列表Adapter的getItemViewType
position = position - mHeaderViews.size();
return mAdapter.getItemViewType(position);
}
看最后一個(gè)復(fù)寫的方法onBindViewHolder()
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
// 計(jì)算一下位置
final int adapterPosition = position - mHeaderViews.size();
mAdapter.onBindViewHolder(holder, adapterPosition);
// 設(shè)置點(diǎn)擊和長(zhǎng)按事件
if (mItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemClickListener.onItemClick(adapterPosition);
}
});
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return mLongClickListener.onLongClick(adapterPosition);
}
});
}
}
/判斷是不是頭部或者底部的位置
/**
* 是不是底部位置
*/
private boolean isFooterPosition(int position) {
return position >= (mHeaderViews.size() + mAdapter.getItemCount());
}
/**
* 是不是頭部位置
*/
private boolean isHeaderPosition(int position) {
return position < mHeaderViews.size();
}
好了,這時(shí)候我們的裝飾的adapter已經(jīng)寫好了,接下來(lái)我們寫下RecylerView的包裹類,不然我們這樣使用
RecyclerAdapter mRealAdapter = new RecyclerAdapter();
WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(mRealAdapter);
// setAdapter
mRecyclerView.setAdapter(wrapRecyclerAdapter);
//添加頭尾部
這這樣使用感覺(jué)很尷尬啊
WrapRecyclerView繼承于RecyclerView內(nèi)部
// 包裹了一層的頭部底部Adapter
private WrapRecyclerAdapter mWrapRecyclerAdapter;
// 這個(gè)是列表數(shù)據(jù)的Adapter
private Adapter mAdapter;
// 增加一些通用功能
// 空列表數(shù)據(jù)應(yīng)該顯示的空View
// 正在加載數(shù)據(jù)頁(yè)面,也就是正在獲取后臺(tái)接口頁(yè)面
private View mEmptyView, mLoadingView;
public WrapRecyclerView(Context context) {
super(context);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
還是先看我們添加和刪除的的四個(gè)方法
// 添加頭部
public void addHeaderView(View view) {
// 如果沒(méi)有Adapter那么就不添加,也可以選擇拋異常提示
// 讓他必須先設(shè)置Adapter然后才能添加,這里是仿照ListView的處理方式
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}
// 添加底部
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}
// 移除頭部
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}
// 移除底部
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}
復(fù)寫setAdapter的方法
@Override
public void setAdapter(Adapter adapter) {
// 為了防止多次設(shè)置Adapter
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mDataObserver);
mAdapter = null;
}
this.mAdapter = adapter;
if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
}
super.setAdapter(mWrapRecyclerAdapter);
// 注冊(cè)一個(gè)觀察者
mAdapter.registerAdapterDataObserver(mDataObserver);
// 解決GridLayout添加頭部和底部也要占據(jù)一行
mWrapRecyclerAdapter.adjustSpanSize(this);
// 加載數(shù)據(jù)頁(yè)面
if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
mLoadingView.setVisibility(View.GONE);
}
if (mItemClickListener != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}
if (mLongClickListener != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}
mDataObserver注冊(cè)器的使用呢
private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
if (mAdapter == null) {
return;
}
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyDataSetChanged();
}
dataChanged();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter == null) return;
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
dataChanged();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
if (mAdapter == null) {
return;
}
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
}
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
if (mAdapter == null) {
return;
}
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemChanged(positionStart);
}
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (mAdapter == null) {
return;
}
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
}
dataChanged();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter == null) {
return;
}
// 觀察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted沒(méi)效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemInserted(positionStart);
}
dataChanged();
}
};
Adapter數(shù)據(jù)改變的方法
private void dataChanged() {
if (mAdapter.getItemCount() == 0) {
// 沒(méi)有數(shù)據(jù)
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
} else {
mEmptyView.setVisibility(GONE);
}
}
}
添加一個(gè)空列表數(shù)據(jù)頁(yè)面和添加一個(gè)正在加載數(shù)據(jù)的頁(yè)面
/**
* 添加一個(gè)空列表數(shù)據(jù)頁(yè)面
*/
public void addEmptyView(View emptyView) {
this.mEmptyView = emptyView;
}
/**
* 添加一個(gè)正在加載數(shù)據(jù)的頁(yè)面
*/
public void addLoadingView(View loadingView) {
this.mLoadingView = loadingView;
mLoadingView.setVisibility(View.VISIBLE);
}