什么是装饰设计模式
看到装饰这两个字就知道是在原来的东西上添加东西,比如一条炒好的鱼(被装饰的对象),装个黄瓜雕饰(装饰),变成一盘菜(装饰后的对象),翻译到程序的世界里就是想扩展一个类的功能,但是这个类呢或许是其他库里的,我们不好修改,于是,我们自己定一个类,这个类中持有想要装饰的也就是扩展功能的类,然后再扩展功能。
注意点:
1.装饰者类要实现真实类同样的接口
2.装饰者类内有一个真实对象的引用(可以通过装饰者类的构造器传入)
3.装饰类对象在主类中接受请求,将请求发送给真实的对象(相当于已经将引用传递到了装饰类的真实对象)
有点像静态代理模式,但是静态代理只代理功能,不扩展功能
下面我们看看怎么通过装饰设计模式扩展recycleView,使它可以像listview一样添加头部和底部
我们很早就接触了Listview,应该都知道ListView的一个android自带的adapter,那就是 HeaderViewListAdapter,源码如下:
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
private final ListAdapter mAdapter;
// These two ArrayList are assumed to NOT be null.
// They are indeed created when declared in ListView and then shared.
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
// Used as a placeholder in case the provided info views are indeed null.
// Currently only used by some CTS tests, which may be removed.
static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
new ArrayList<ListView.FixedViewInfo>();
boolean mAreAllFixedViewsSelectable;
private final boolean mIsFilterable;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
if (headerViewInfos == null) {
mHeaderViewInfos = EMPTY_INFO_LIST;
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = EMPTY_INFO_LIST;
} else {
mFooterViewInfos = footerViewInfos;
}
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
public boolean isEmpty() {
return mAdapter == null || mAdapter.isEmpty();
}
private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
if (infos != null) {
for (ListView.FixedViewInfo info : infos) {
if (!info.isSelectable) {
return false;
}
}
}
return true;
}
public boolean removeHeader(View v) {
for (int i = 0; i < mHeaderViewInfos.size(); i++) {
ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
if (info.view == v) {
mHeaderViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public boolean removeFooter(View v) {
for (int i = 0; i < mFooterViewInfos.size(); i++) {
ListView.FixedViewInfo info = mFooterViewInfos.get(i);
if (info.view == v) {
mFooterViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public int getCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
public boolean areAllItemsEnabled() {
if (mAdapter != null) {
return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
} else {
return true;
}
}
public boolean isEnabled(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).isSelectable;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.isEnabled(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
}
public Object getItem(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).data;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItem(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
public long getItemId(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemId(adjPosition);
}
}
return -1;
}
public boolean hasStableIds() {
if (mAdapter != null) {
return mAdapter.hasStableIds();
}
return false;
}
public View getView(int position, View convertView, ViewGroup parent) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).view;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
public int getItemViewType(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
public int getViewTypeCount() {
if (mAdapter != null) {
return mAdapter.getViewTypeCount();
}
return 1;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
public Filter getFilter() {
if (mIsFilterable) {
return ((Filterable) mAdapter).getFilter();
}
return null;
}
public ListAdapter getWrappedAdapter() {
return mAdapter;
}
}
其实这个类就是一个装饰模式的实现,扩展了ListAdapter,使ListAdapter具有了添加头部和底部的功能,我们完全可以模仿它来实现RecycleView的添加头部局和底部局的adapter
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// 原来的RecyclerView.Adapter,并不支持头部和底部的添加
private final RecyclerView.Adapter mRealAdapter;
ArrayList<View> mHeaderViews;
ArrayList<View> mFooterViews;
public WrapRecyclerAdapter(RecyclerView.Adapter realAdapter) {
this.mRealAdapter = realAdapter;
mRealAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
notifyDataSetChanged();
}
});
mHeaderViews = new ArrayList<>();
mFooterViews = new ArrayList<>();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
// 问题如果想知道是哪个部分,必须要知道 position 也就是位置 ,但是目前只有 viewType,直接在 getItemViewType中返回将postion作为type的值返回,这样当前
函数的第二个参数就是position了
// 头部返回 头部的ViewHolder
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return createHeaderFooterViewHolder(mHeaderViews.get(position));
}
// mRealAdapter 返回 mRealAdapter的ViewHolder
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mRealAdapter.onCreateViewHolder(parent, mRealAdapter.getItemViewType(adjPosition));
}
}
// 底部返回 底部的ViewHolder
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return createHeaderFooterViewHolder(mFooterViews.get(adjPosition - adapterCount));
}
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
public int getHeadersCount() {
return mHeaderViews.size();
}
public int getFootersCount() {
return mFooterViews.size();
}
@Override
public int getItemViewType(int position) {
// 把位置作为 viewType
return position;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// 这个方法先不写,测试一下
// 头部和底部是都不需要做处理的,只要 mRealAdapter 要去做处理
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return;
}
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
mRealAdapter.onBindViewHolder(holder, adjPosition);
}
}
}
@Override
public int getItemCount() { // 总共返回多少条 = 底部条数+头部条数+真实的Adapter条数
return mFooterViews.size() + mHeaderViews.size() + mRealAdapter.getItemCount();
}
/**
* 添加头部
*
* @param view
*/
public void addHeaderView(View view) {
if (!mHeaderViews.contains(view)) {
mHeaderViews.add(view);
notifyDataSetChanged();
}
}
/**
* 添加底部
*
* @param view
*/
public void addFooterView(View view) {
if (!mFooterViews.contains(view)) {
mFooterViews.add(view);
notifyDataSetChanged();
}
}
/**
* 移除头部
*
* @param view
*/
public void removeHeaderView(View view) {
if (mHeaderViews.contains(view)) {
mHeaderViews.remove(view);
notifyDataSetChanged();
}
}
/**
* 移除底部
*
* @param view
*/
public void removeFooterView(View view) {
if (mFooterViews.contains(view)) {
mFooterViews.remove(view);
notifyDataSetChanged();
}
}
}
public class WrapRecyclerView extends RecyclerView{
private WrapRecyclerAdapter mAdapter;
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);
}
@Override
public void setAdapter(Adapter adapter) {
mAdapter = new WrapRecyclerAdapter(adapter);
super.setAdapter(mAdapter);
}
/**
* 添加头部
* @param view
*/
public void addHeaderView(View view){
// 必须要设置 Adapter 之后才能添加头部和底部
if(mAdapter != null){
mAdapter.addHeaderView(view);
}
}
/**
* 添加底部
* @param view
*/
public void addFooterView(View view){
if(mAdapter != null){
mAdapter.addFooterView(view);
}
}
/**
* 移除头部
* @param view
*/
public void removeHeaderView(View view){
if(mAdapter != null){
mAdapter.removeHeaderView(view);
}
}
/**
* 移除底部
* @param view
*/
public void removeFooterView(View view){
if(mAdapter != null){
mAdapter.removeFooterView(view);
}
}
}
是不是很简单,在我们的装饰器类中,只要根据不同的positon处理不同的返回布局就好了
使用如下:
public class MainActivity extends AppCompatActivity {
private WrapRecyclerView mRecyclerView;
private List<Integer> mItems;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mItems = new ArrayList<>();
for (int i=0;i<20;i++){
mItems.add(i);
}
mRecyclerView = (WrapRecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(new RecyclerAdapter());
//添加顶布局和底布局
View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header_view,mRecyclerView,false);
mRecyclerView.addHeaderView(headerView);
View footerView = LayoutInflater.from(this).inflate(R.layout.layout_header_view,mRecyclerView,false);
mRecyclerView.addFooterView(headerView);
}
private class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_rv,parent,false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
Log.v("rec",position+"");
holder.text.setText("position = "+mItems.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItems.remove(position);
notifyDataSetChanged();
}
});
}
@Override
public int getItemCount() {
return mItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView text;
public ViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text);
}
}
}
}