代码中的设计模式4----装饰器模式(万能适配器、RecyclerView添加头部和底部View)

装饰器模式,在日常的生活中是极为常见的一个场景。例如给房子装修,需要在新房中添加一些配置,这其实就是一个具体的装饰器模式。

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,等有时间,可以将这部分代码实现贴出来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Awesome_lay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值