装饰器模式,在日常的生活中是极为常见的一个场景。例如给房子装修,需要在新房中添加一些配置,这其实就是一个具体的装饰器模式。
1 装饰器模式的对象
一般来说,装饰器模式需要的对象大概有4种,拿装修房子这个例子来说:
(1)抽象组件Component:待装饰的抽象组件,可以是抽象类,也可以是接口;
(2)具体组件ConcreteComponent:具体的待装饰对象,例如待装修的房子。
(3)抽象装饰者AbstractDecorator:主要作用修饰组件,因此内部需要有一个组件的引用,可根据场景创建具体的装饰者。
(4)具体装饰者ConcreteDecorator:例如家具、瓷砖、书架…
/**
* 抽象组件(待装饰对象)
*/
public interface IComponent {
void operate();
}
/**
* 具体的被装饰者 房子
*/
public class MyHouse implements IComponent{
@Override
public void operate() {
System.out.println("这是我的房子");
}
}
public abstract class AbstractDecorator implements IComponent{
//内部持有组件的引用
private IComponent component;
public AbstractDecorator(IComponent component){
this.component = component;
}
/**
* 抽象装饰者,基础的修饰
*/
public void operate(){
component.operate();
}
}
具体的装饰者,比如我要买一套家具,我要重新贴瓷砖…
public class FurnitureDecorator extends AbstractDecorator{
public FurnitureDecorator(IComponent component) {
//调用了父类的构造方法
super(component);
}
@Override
public void operate() {
//调用父类的operate方法,其实就是调用了具体被装饰者对象的operate方法
super.operate();
//做具体的装饰处理
operateFurniture();
}
private void operateFurniture() {
//
System.out.println("我购置了一套家具!!!");
}
}
//具体的被装饰者对象
IComponent component = new MyHouse();
//使用FurnitureDecorator 来装饰我的房子
FurnitureDecorator furnitureDecorator = new FurnitureDecorator(component);
//先调用MyHouse的operate方法,再执行具体的装饰任务
furnitureDecorator.operate();
执行结果:
这是我的房子
我购置了一套家具!!!
2 Android中的装饰器模式
在Android源码中,存在大量的装饰器设计模式,在实际的工作中,例如给RecyclerView添加头部和尾部,就是装饰器模式。原生的RecyclerView就是组件(被修饰的对象),头部和尾部就是具体的装饰者。
原生RecyclerView不支持头部尾部,因此需要装饰者去修饰RecyclerView,因此需要持有原有RecyclerView的引用。
基本的RecyclerView的适配器
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recycler_item,parent,false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//加载数据
holder.tv_recycler_item.setText("item "+position);
}
@Override
public int getItemCount() {
return 100;
}
class ViewHolder extends RecyclerView.ViewHolder{
TextView tv_recycler_item;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tv_recycler_item = itemView.findViewById(R.id.tv_recycler_item);
}
}
}
如果要加头部和尾部,需要对RecyclerViewAdapter 做扩展,RecyclerViewAdapter 就相当于具体的组件ConcreteComponent。
public class WrapRecyclerView extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//持有扩展适配器的引用,对其进行扩展
private RecyclerViewAdapter mComponentAdapter;
//头部View集合
private List<View> headView;
private List<View> footView;
public WrapRecyclerView(RecyclerViewAdapter mComponentAdapter){
this.mComponentAdapter = mComponentAdapter;
headView = new ArrayList<>();
footView = new ArrayList<>();
}
/**
*
* @param parent
// * @param viewType 可修改为position,但是需要重写getItemViewType
@param position item的位置
* @return
*/
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
//根据位置position判断,加载哪部分的View
if(position < headView.size()){
//如果加载View的position小于headView的size,就加载headView的ViewHolder,一般都会展示出来
return createHeadViewHolder(headView.get(position));
}
//判断是否是中间View的展示
int startPosition = position - headView.size();
if(mComponentAdapter != null){
int totalPosition = mComponentAdapter.getItemCount();
if(startPosition < totalPosition){
//展示中间的View
return mComponentAdapter.onCreateViewHolder(parent,mComponentAdapter.getItemViewType(position));
}
}
//剩下就是底部View的展示(position = 当前位置 - 头部个数- mComponentAdapter count)
return createFootViewHolder(footView.get(startPosition - mComponentAdapter.getItemCount()));
}
private RecyclerView.ViewHolder createFootViewHolder(View view) {
return new RecyclerView.ViewHolder(view){};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
//底部和头部是不需要去处理的
if(position < headView.size()){
return;
}
int startPosition = position - headView.size();
if(mComponentAdapter != null){
int totalPosition = mComponentAdapter.getItemCount();
if(startPosition < totalPosition){
//展示中间的View
mComponentAdapter.onBindViewHolder((RecyclerViewAdapter.ViewHolder) holder,position);
}
}
}
private RecyclerView.ViewHolder createHeadViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {};
}
//多种不同ItemView,需要重写该方法,适配不同UI
//不重写该方法,则抛出异常
@Override
public int getItemViewType(int position) {
//把位置作为ViewType,传入ViewType得到的是位置
return position;
}
//头部View的个数 + 待修饰组件的View个数 + 底部View的个数
@Override
public int getItemCount() {
return headView.size() + mComponentAdapter.getItemCount() + footView.size();
}
public void addHeadView(View view){
if(!headView.contains(view)){
headView.add(view);
notifyDataSetChanged();
}
}
public void removeHeadView(View view){
if(headView.contains(view)){
headView.remove(view);
notifyDataSetChanged();
}
}
public void addFootView(View view){
if(!footView.contains(view)){
footView.add(view);
notifyDataSetChanged();
}
}
public void removeFootView(View view){
if(footView.contains(view)){
footView.remove(view);
notifyDataSetChanged();
}
}
}
在使用时,将RecyclerViewAdapter实例化,作为待装饰的对象,WrapRecyclerView将RecyclerViewAdapter包了一层,做扩展,最终实现RecyclerView的头部和尾部添加。
mAdapter = new RecyclerViewAdapter();
wrapRecyclerView = new WrapRecyclerView(mAdapter);
rv_decorator.setLayoutManager(new LinearLayoutManager(this));
// View headView = View.inflate(this,R.layout.layout_head_view,null);
View headView = LayoutInflater.from(this).inflate(R.layout.layout_head_view,rv_decorator,false);
wrapRecyclerView.addHeadView(headView);
rv_decorator.setAdapter(wrapRecyclerView);
这种使用方式是不是很眼熟,在IO流中,InputStream和OutputStream作为基准对象,FileInputStream、BufferedInputStream等等将其包装,其实这也是一种装饰器模式。
接着上面的问题,如果没有重写getItemViewType方法,就会抛出异常:
ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
看下源码
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
try {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
if (holder.itemView.getParent() != null) {
throw new IllegalStateException("ViewHolder views must not be attached when"
+ " created. Ensure that you are not passing 'true' to the attachToRoot"
+ " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
}
holder.mItemViewType = viewType;
return holder;
} finally {
TraceCompat.endSection();
}
}
createViewHolder方法中,传入的第二个参数是viewType,但是在onCreateViewHolder方法中,传入的是position,最终重写getItemViewType方法是为了根据viewType得到position,调用onCreateViewHolder方法,会走到这里,否则会抛出异常。
所以还有一种方式,添加RecyclerView的头部和尾部,就是在getItemViewType中,根据位置来获取ViewType,一共3种ViewType,分别是头部、尾部、中间的原生RecyclerView,等有时间,可以将这部分代码实现贴出来。