美文网首页安卓基础
装饰模式---RecyclerView添加头部和底部

装饰模式---RecyclerView添加头部和底部

作者: 加个标志位 | 来源:发表于2019-07-30 09:03 被阅读0次

    一. 装饰模式
    装饰模式指的是在不必改变原类文件并且不使用继承的情况下,通过创建一个包装对象(也就是装饰包裹真实的对象),动态地扩展一个对象的功能。比如在软件开发过程中,通过使用一些现存的组件(这些组件可能只是完成了一些核心功能或是不能满足目前开发的需求),在不改变其结构的情况下动态地扩展其功能。这种处理的方式就是釆用装饰模式来实现的。

    1.定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,是结构型设计模式中的一种。


    装饰模式.png

    • Component:抽象组件,可以是接口或是抽象类,被装饰的最原始的对象。
    • ConcreteComponent:组件具体实现类。Component的具体实现类,被装饰的具体对象。
    • Decorator:抽象装饰者,从外类来拓展Component类的功能,但对于Component来说无须知道Decorator的存在。但是在Decorator的属性中必然有一个private变量指向Component抽象组件。
    • ConcreteDecorator:装饰者的具体实现类。

    2.装饰模式的使用场景和优缺点
    (1)使用场景:
    • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
    • 在不能采用继承的方式对某一个类进行扩充或者采用继承不利于该类扩展和维护时。
    (2)优点:
    • 通过组合而非继承的方式,动态地扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
    • 有效避免了使用继承的方式扩展对象功能而带来的灵活性差、子类无限制扩张的问题。
    • 具体组件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开放封闭原则”。
    (3)缺点:
    因为所有对象均继承于Component,所以如果Component内部结构发生改变,则不可避免地影响所有子类(装饰者和被装饰者)。比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很难。对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。所以,只在必要的时候使用装饰模式。建议装饰层数不宜过多,否则会影响效率。

    1. 一个小例子
      今天出场的仍然是我们的主角彪志伟同学,经过几个月的不懈努力,彪志伟的女神终于答应和他共进晚餐,所以今天彪志伟同学决定好好装扮一下自己,以给女神留下一个绅士般的印象。
      首先是抽象组件类Gentleman:
    package com.example.tcl.myapplication.decoratemodel;
    
    /**
     * 抽象组件,可以是抽象类,也可以是接口;
     */
    public abstract class Gentleman {
        public abstract void dressUp();
        public abstract void afterDinner();
    
        public void execute(){
    
        }
    }
    

    然后就是我们的吊丝同学彪志伟:

    package com.example.tcl.myapplication.decoratemodel;
    
    import android.util.Log;
    
    /**
     * 抽象组件的具体实现类
     */
    public class BiaoZhiwei extends Gentleman {
        private String TAG = "BiaoZhiWei";
        @Override
        public void dressUp() {
            Log.i(TAG, "彪志伟打扮成绅士模样!");
        }
    
        @Override
        public void afterDinner() {
            Log.i(TAG, "彪志伟送女神回家,然后去了网吧和朋友开黑打游戏");
        }
    
        @Override
        public void execute() {
            super.execute();
            dressUp();
            afterDinner();
        }
    }
    

    彪志伟早早就到了约会地点,然后他又想到去买束花,提前定好餐厅(这里相当于BiaoZhiwei这个类的功能的拓展):

    抽象装饰类
    package com.example.tcl.myapplication.decoratemodel;
    /**
     * 抽象装饰类
     */
    public abstract class DecorateGentleman extends Gentleman{
        private Gentleman mGentleman;
        public DecorateGentleman(Gentleman gentleman) {
            this.mGentleman = gentleman;
        }
    
        public abstract void buyFlowers();
        public abstract void reserveDinner();
    
        @Override
        public void dressUp() {
            mGentleman.dressUp();
        }
    
        @Override
        public void afterDinner() {
            mGentleman.afterDinner();
        }
    }
    

    装饰者的具体实现类

    package com.example.tcl.myapplication.decoratemodel;
    import android.util.Log;
    
    public class BzwDecorateGentleman extends DecorateGentleman {
        private String TAG = "BiaoZhiwei";
    
        public BzwDecorateGentleman(Gentleman gentleman) {
            super(gentleman);
        }
    
        @Override
        public void buyFlowers() {
            Log.i(TAG, "彪志伟想到初次和女神共进晚餐,便去买了一束花");
        }
    
        @Override
        public void reserveDinner() {
            Log.i(TAG, "买完花,顺便把晚餐订好");
        }
    
        @Override
        public void execute() {
            super.execute();
            dressUp();
            buyFlowers();
            reserveDinner();
            afterDinner();
        }
    }
    

    使用和结果:

    Gentleman bzw = new BiaoZhiwei();
    BzwDecorateGentleman bzwDec = new BzwDecorateGentleman(bzw);
    bzwDec.execute();
    Log信息:
    com.example.tcl.myapplication I/BiaoZhiwei:
        彪志伟打扮成绅士模样!
        彪志伟想到初次和女神共进晚餐,便去买了一束花
        买完花,顺便把晚餐订好
        彪志伟送女神回家,然后去了网吧和朋友开黑打游戏
    

    备注:装饰设计模式中,一般情况把需要扩展功能的类作为构造参数传递。

    二. 给RecyclerView添加头部和底部
    首先,我们的目的是添加头部和底部的显示,而RecyclerView的显示都是在它的Adapter中完成的;所以我们要明确进行功能拓展的是RecyclerView的Adapter,也就是说我们需要把这个Adapter作为构造参数传递到装饰类中。

    1. 首先是用来装饰传入adpter的WrapRecyclerAdapter,其中就有定义添加和删除我们头部和底部的方法。
    package com.example.tcl.recy.recyclerviewdec;
    
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;
    
    import java.util.ArrayList;
    
    /**
     * 使用装饰模式的RecyclerView.Adapter,需要将一个RecyclerView.Adapter作为参数传入
     */
    public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
        private RecyclerView.Adapter<RecyclerView.ViewHolder> mAdapter;
        private ArrayList<View> mHeadViews;
        private ArrayList<View> mFootViews;
    
        //把RecyclerView的Adapter作为参数传入,对其进行功能扩展,使其支持头部和底部
        public WrapRecyclerAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
            this.mAdapter = adapter;
            //注册监听,传入adapter的notifyDataSetChanged时会通知WrapRecyclerAdapter数据变化了
            mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
                @Override
                public void onChanged() {
                    notifyDataSetChanged();
                }
            });
            mHeadViews = new ArrayList<>();
            mFootViews = new ArrayList<>();
        }
    
        /***
         * 根据不同的位置返回不同的ViewHolder,但是onCreateViewHolder方法中并没有获取位置的信息,只能得
         * 到
         * - - -> 可以通过重写getItemViewType方法,把position直接返回即可。
         * viewGroup表示parent; i 表示返回的ItemViewType的类型
         */
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
            //如果是头部  返回头部的ViewHolder;
            int headCounts = mHeadViews.size();
            if (position < headCounts){
                //return createHeadFootViewHolder(mHeadViews.get(position));
                return new RecyclerView.ViewHolder(mHeadViews.get(position)) {};
            }
    
            //如果是出入的Adapter  返回Adapter的ViewHolder;
            int adapterCounts = position - headCounts;
            int adapterCountsAfferent = 0;
            if (mAdapter != null){
                adapterCountsAfferent = mAdapter.getItemCount();
                if (adapterCounts < adapterCountsAfferent){
                    //不能直接传入position,否则会出现mAdapter中布局不兼容的情况,因为我们重写了getItemViewType方法,
                    //直接将position返回,本来返回的是super.getItemViewType(position);
                    return mAdapter.createViewHolder(viewGroup, mAdapter.getItemViewType(adapterCounts));
                }
            }
            //如果是底部  返回底部的ViewHolder;
            return new RecyclerView.ViewHolder(mFootViews.get(adapterCounts - adapterCountsAfferent)) {};
        }
    
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
            //头部底部不做任何处理
            //如果是头部
            int headCounts = mHeadViews.size();
            if (position < headCounts){
                //return createHeadFootViewHolder(mHeadViews.get(position));
                return;
            }
    
            //如果是出入的Adapter  则通过传入的Adapter中的onBindViewHolder来处理
            int adapterCounts = position - headCounts;
            int adapterCountsAfferent = 0;
            if (mAdapter != null){
                adapterCountsAfferent = mAdapter.getItemCount();
                if (adapterCounts < adapterCountsAfferent){
                    mAdapter.onBindViewHolder(viewHolder, position);
                }
            }
            //如果是底部  返回底部的ViewHolder;
        }
    
        @Override
        public int getItemCount() {
            //返回的条目 = 传入的adapter的条目 + 头部条目 + 底部条目
            return mAdapter.getItemCount() + mHeadViews.size() + mFootViews.size();
        }
    
        /**
         * 重写getItemViewType 把position直接返回,以便onCreateViewHolder中获取到viewHolder的类型
         */
        @Override
        public int getItemViewType(int position) {
            return position;
        }
    
    
    
        //添加头部
        public void addHeadView(View view){
            if (!mHeadViews.contains(view)){
                mHeadViews.add(view);
                notifyDataSetChanged();
            }
        }
    
        //移除头部
        public void removeHeadView(View view){
            if (mHeadViews.contains(view)){
                mHeadViews.remove(view);
                notifyDataSetChanged();
            }
        }
    
        //添加底部
        public void addFootView(View view){
            if (!mFootViews.contains(view)){
                mFootViews.add(view);
                notifyDataSetChanged();
            }
        }
    
        //移除底部
        public void removeFootView(View view){
            if (!mFootViews.contains(view)){
                mFootViews.remove(view);
                notifyDataSetChanged();
            }
        }
    }
    
    
    1. 重新封装一下RecyclerView,参照ListView添加头部和底部信息的写法,把添加的功能封装在RecyclerView。
    package com.example.tcl.recy.recyclerviewdec;
    
    import android.content.Context;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class WrapRecyclerView extends RecyclerView {
        private WrapRecyclerAdapter mAdapter;
    
        public WrapRecyclerView(@NonNull Context context) {
            super(context);
        }
    
        public WrapRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public WrapRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public void setAdapter(@Nullable Adapter adapter) {
            mAdapter = new WrapRecyclerAdapter(adapter);
            super.setAdapter(mAdapter);
        }
    
        public void addHeadVier(View view){
            if (mAdapter != null){
                mAdapter.addHeadView(view);
            }
        }
    
        public void addFootVier(View view){
            if (mAdapter != null){
                mAdapter.addFootView(view);
            }
        }
    
        public void removeHeadView(View view){
            if (mAdapter != null){
                mAdapter.removeHeadView(view);
            }
        }
    
        public void removeFootView(View view){
            if (mAdapter != null){
                mAdapter.removeFootView(view);
            }
        }
    }
    

    以上只实现了基本的添加头部和底部的功能,在开发时可以根据具体的需求进行进一步修改和完善。

    相关文章

      网友评论

        本文标题:装饰模式---RecyclerView添加头部和底部

        本文链接:https://www.haomeiwen.com/subject/zktgrctx.html