定义
在不使用继承的前提下,动态的扩展一个类的功能,就叫做装饰设计模式。
背景
android里面使用装饰设计模式的有Context,ListView添加和删除头部尾部布局,还有IO流等等。那么到底如何动态的扩展一个类的功能呢?
实例
recyclerview在项目当中是使用的最多的控件之一,经常要添加头部或者尾部,那么如何一劳永逸的让recyclerview都能支持头部和尾部布局呢?- 第一步
首先继承自Recyclerview.Adapter,并且用两个集合来装载头部和尾部布局
public class DecorRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// 原来的recyclerview.Adapter,并不支持头部和底部的添加
private RecyclerView.Adapter mRealAdapter;
private List<View> mHeadViewList = new ArrayList<>();
private List<View> mFooterList = new ArrayList<>();
public DecorRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mRealAdapter = adapter;
}
public void addHeadView(View headView) {
if (!mHeadViewList.contains(headView)) {
mHeadViewList.add(headView);
notifyDataSetChanged();
}
}
public void addFooterView(View footerView) {
if (!mFooterList.contains(footerView)) {
mFooterList.add(footerView);
notifyDataSetChanged();
}
}
public void removeHeadView(View headView) {
if (mHeadViewList.contains(headView)) {
mHeadViewList.remove(headView);
notifyDataSetChanged();
}
}
public void removeFooterView(View footerView) {
if (mFooterList.contains(footerView)) {
mFooterList.remove(footerView);
notifyDataSetChanged();
}
}
}
因为我们将头部和尾部布局添加到recyclerview中了,所以返回的getItemCount就需要修改成下边这样
@Override public int getItemCount() {
return mHeadViewList.size() + mRealAdapter.getItemCount() + mFooterList.size();
}
- 第二步
当我们使用onCreateViewHolder方法的时候,需要区分是头部,还是真实的Adapter还是尾部:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
// 头部返回头部的viewHolder
// 真实的adapter返回真实的viewHolder
// 底部返回底部的viewHolder
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return createHeaderOrFooterViewHolder(mHeadViewList.get(position));
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mRealAdapter.onCreateViewHolder(parent, mRealAdapter.getItemViewType(adjPosition));
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return createHeaderOrFooterViewHolder(mFooterList.get(adjPosition - adapterCount));
}
private RecyclerView.ViewHolder createHeaderOrFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {};
}
public int getHeadersCount() {
return mHeadViewList.size();
}
/***
* 将position作为viewType传递给onCreateViewHolder
*
* @param position
* 位置
* @return viewType
*/
@Override public int getItemViewType(int position) {
return position;
}
- 第三步
在onBindViewHolder的时候,如果是头部或尾部,则无需处理
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
mRealAdapter.onBindViewHolder(holder, position);
}
}
}
- 第四步
其实上边的代码就已经能够使用了,但是使用的时候,每一个recyclerview设置adapter的时候,都需要包装在DecorRecyclerviewAdapter里面,所以我们需要包装一下Recyclerview。
public class DecorRecyclerview extends RecyclerView {
private DecorRecyclerAdapter decorRecyclerAdapter;
public DecorRecyclerview(Context context) {
this(context, null);
}
public DecorRecyclerview(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DecorRecyclerview(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override public void setAdapter(Adapter adapter) {
decorRecyclerAdapter = new DecorRecyclerAdapter(adapter);
super.setAdapter(decorRecyclerAdapter);
}
public void addHeadView(View view) {
if (decorRecyclerAdapter != null) {
decorRecyclerAdapter.addHeadView(view);
}
}
public void addFooterView(View view) {
if (decorRecyclerAdapter != null) {
decorRecyclerAdapter.addFooterView(view);
}
}
}
- 使用
在MainActivity中使用如下
/**
* // 业务逻辑层,能分开就分开,比如说注册页面和绑定用户手机页面,就是非常相似的业务逻辑,一定不要去封装 // 不包含业务逻辑的,一定要封装
* Created by wuxiaojun on 2018/11/22.
*/
public class RecyclerviewActivity extends Activity {
private DecorRecyclerview id_recyclerview;
private List<String> mList = new ArrayList<>();
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
for (int i = 0; i < 100; i++) {
mList.add("position=" + i);
}
id_recyclerview = (DecorRecyclerview) findViewById(R.id.id_recyclerview);
id_recyclerview.addItemDecoration(new DividerItemDecoration(this, LinearLayout.VERTICAL));
id_recyclerview.setLayoutManager(new LinearLayoutManager(this));
MyAdapter adapter = new MyAdapter();
id_recyclerview.setAdapter(adapter);
View view = LayoutInflater.from(this).inflate(R.layout.item_head, id_recyclerview, false);
id_recyclerview.addHeadView(view);
}
private class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
@Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
return new MyViewHolder(view);
}
@Override public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.textView.setText("position=" + mList.get(position));
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
mList.remove(position);
notifyDataSetChanged();
}
});
}
@Override public int getItemCount() {
return mList.size();
}
}
private class MyViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.id_info);
}
}
}
如上,装饰设计模式就已经完成了,其实在不继承的前提下,扩展A类的功能就是通过构造方法将A作为参数传递到DecorA中,然后再DecorA里面添加一些A所没有的功能。就是这么简单。
所以代码如下
public class DecorRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// 原来的recyclerview.Adapter,并不支持头部和底部的添加
private RecyclerView.Adapter mRealAdapter;
private List<View> mHeadViewList = new ArrayList<>();
private List<View> mFooterList = new ArrayList<>();
public DecorRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mRealAdapter = adapter;
// 当在mRealAdapter中改变数据的时候,得让DecorRecyclerAdapter也刷新
mRealAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override public void onChanged() {
notifyDataSetChanged();
}
});
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
// 头部返回头部的viewHolder
// 真实的adapter返回真实的viewHolder
// 底部返回底部的viewHolder
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return createHeaderOrFooterViewHolder(mHeadViewList.get(position));
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mRealAdapter.onCreateViewHolder(parent, mRealAdapter.getItemViewType(adjPosition));
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return createHeaderOrFooterViewHolder(mFooterList.get(adjPosition - adapterCount));
}
private RecyclerView.ViewHolder createHeaderOrFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {};
}
public int getHeadersCount() {
return mHeadViewList.size();
}
/***
* 将position作为viewType传递给onCreateViewHolder
*
* @param position
* 位置
* @return viewType
*/
@Override public int getItemViewType(int position) {
return position;
}
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
mRealAdapter.onBindViewHolder(holder, position);
}
}
}
@Override public int getItemCount() {
return mHeadViewList.size() + mRealAdapter.getItemCount() + mFooterList.size();
}
public void addHeadView(View headView) {
if (!mHeadViewList.contains(headView)) {
mHeadViewList.add(headView);
notifyDataSetChanged();
}
}
public void addFooterView(View footerView) {
if (!mFooterList.contains(footerView)) {
mFooterList.add(footerView);
notifyDataSetChanged();
}
}
public void removeHeadView(View headView) {
if (mHeadViewList.contains(headView)) {
mHeadViewList.remove(headView);
notifyDataSetChanged();
}
}
public void removeFooterView(View footerView) {
if (mFooterList.contains(footerView)) {
mFooterList.remove(footerView);
notifyDataSetChanged();
}
}
}