設(shè)計(jì)模式——裝飾設(shè)計(jì)模式

什么是裝飾設(shè)計(jì)模式姻政?

裝飾器模式是一種用于代替繼承的技術(shù)呆抑,無需通過繼承增加子類就能擴(kuò)展對象的新功能。使用對象的關(guān)聯(lián)關(guān)系代替繼承關(guān)系汁展,更加靈活鹊碍,同時避免類型體系的快速膨脹。
在不使用的繼承的方式下食绿,采用裝飾設(shè)計(jì)模式可以擴(kuò)展一個對象的功能侈咕,可以使一個對象變得越來越強(qiáng)大。

裝飾模式(Decorator)也叫包裝器模式(Wrapper)
裝飾模式降低系統(tǒng)的耦合度器紧,可以動態(tài)的增加或刪除對象的職責(zé)耀销,并使得需要裝飾的具體構(gòu)建類和具體裝飾類可以獨(dú)立變化,以便增加新的具體構(gòu)建類和具體裝飾類铲汪。

參考文章:
https://www.cnblogs.com/zhangtianq/p/6091047.html
https://blog.csdn.net/android_zyf/article/details/68343953
https://www.cnblogs.com/pecool/p/9534568.html

以普通手機(jī)和智能手機(jī)為例子來講:

Phone類:

public interface Phone {

    void call();

    void sms();
}

普通手機(jī)NormalPhone類:

public class NormalPhone implements Phone {
    @Override
    public void call() {
        System.out.println("可以打電話");
    }

    @Override
    public void sms() {
        System.out.println("可以發(fā)短信");
    }
}

智能手機(jī)SmartPhone類:

public class SmartPhone implements Phone {
    private NormalPhone mPhone;

    SmartPhone(NormalPhone phone) {
        this.mPhone = phone;
    }

    @Override
    public void call() {
        mPhone.call();
        System.out.println("打電話的同時還可以發(fā)微信熊尉、看視頻、玩游戲等等....");
    }

    @Override
    public void sms() {
        mPhone.sms();
    }
}

Client類:

public class Client {
    public static void main(String[] args) {
        NormalPhone normalPhone = new NormalPhone();
        normalPhone.call();
        System.out.println("-----------------");
        SmartPhone smartPhone = new SmartPhone(normalPhone);
        smartPhone.call();
    }
}

控制臺輸出:

可以打電話
-----------------
可以打電話
打電話的同時還可以發(fā)微信掌腰、看視頻狰住、玩游戲等等....

使用裝飾設(shè)計(jì)模式實(shí)現(xiàn)給RecyclerView添加頭部和尾部

首先要知道RecyclerView控件如何使用,可以參考下面這篇文章:
http://www.reibang.com/p/4f9591291365

MainActivity類:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);

        ArrayList<String> datas = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            datas.add(" data " + i);
        }


        recyclerView = findViewById(R.id.recyclerview);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //設(shè)置布局管理器
        recyclerView.setLayoutManager(layoutManager);
        //設(shè)置為垂直布局齿梁,這也是默認(rèn)的
        layoutManager.setOrientation(OrientationHelper.VERTICAL);
        //設(shè)置Adapter
        Adapter recycleAdapter = new Adapter(datas);


        WrapperRecyclerAdapter adapter = new WrapperRecyclerAdapter(recycleAdapter);

        recyclerView.setAdapter(adapter);

        //View headerView = LayoutInflater.from(this).inflate(R.layout.header_view, null);//todo 這樣寫效果不對
        View headerView = LayoutInflater.from(this).inflate(R.layout.header_view, recyclerView, false);
        View footerView = LayoutInflater.from(this).inflate(R.layout.footer_view, recyclerView, false);

        adapter.addHeaderView(headerView);
        adapter.addFooterView(footerView);
        //設(shè)置分隔線
        //recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
        //設(shè)置增加或刪除條目的動畫
        recyclerView.setItemAnimator(new DefaultItemAnimator());

    }


}

Adapter類:

public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {


    private List<String> mDatas;

    public Adapter(List<String> data) {
        this.mDatas = data;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {

        Log.d("TAG", "onBindViewHolder:" + position);

        holder.mTextView.setText(mDatas.get(position));
        holder.mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("TAG", "onClick :" + position);
                mDatas.remove(position);
                notifyDataSetChanged();
            }
        });

    }


    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView mTextView;

        public ViewHolder(View v) {
            super(v);
            mTextView = v.findViewById(R.id.tv_title);
        }
    }
}

WrapperRecyclerAdapter類:

public class WrapperRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private RecyclerView.Adapter mRecyclerAdapter;//原始的Adapter催植,不具有添加header和footer
    ArrayList<View> mHeaderViews;
    ArrayList<View> mFooterViews;

    public WrapperRecyclerAdapter(RecyclerView.Adapter adapter) {
        this.mRecyclerAdapter = adapter;
        mHeaderViews = new ArrayList<>();
        mFooterViews = new ArrayList<>();
        mRecyclerAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                notifyDataSetChanged();
            }
        });
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int postion) {
        Log.e("TAG", "onCreateViewHolder " + postion);
        if (postion < mHeaderViews.size()) {
            Log.e("TAG", "add header view");
            View headerView = mHeaderViews.get(postion);
            return createHeaderView(headerView);
        }

        int itemCount = mRecyclerAdapter.getItemCount();
        int adjPos = postion - mHeaderViews.size();
        if (adjPos < itemCount) {
            Log.e("TAG", "add item view");
            return mRecyclerAdapter.onCreateViewHolder(parent, mRecyclerAdapter.getItemViewType(adjPos));
        }
        Log.e("TAG", "add footer view");
        // footerview
        View footerView = mFooterViews.get(postion - itemCount - mHeaderViews.size());
        return createHeaderView(footerView);
    }

    private RecyclerView.ViewHolder createHeaderView(View view) {
        return new RecyclerView.ViewHolder(view) {
        };
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        Log.d("TAG", "Wrapper RecyclerAdapter onBindViewHolder:" + position);

        if (position < mHeaderViews.size()) {
            return;
        }

        int adjPos = position - mHeaderViews.size();
        if (adjPos < mRecyclerAdapter.getItemCount()) {
            mRecyclerAdapter.onBindViewHolder(holder, adjPos);
            //mRecyclerAdapter.onBindViewHolder(holder, adjPos);
        } else {
            Log.d("TAG", "adjPos getItemCount." + adjPos + "  " + mRecyclerAdapter.getItemCount());
            //fix footer view.

        }


    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    @Override
    public int getItemCount() {
        return mRecyclerAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
    }

    // 添加頭部
    public void addHeaderView(View v) {
        if (!mHeaderViews.contains(v)) {
            mHeaderViews.add(v);
            notifyDataSetChanged();
        }
    }

    public void removeHeaderView(View v) {
        if (mHeaderViews.contains(v)) {
            mHeaderViews.remove(v);
            notifyDataSetChanged();
        }
    }

    // 添加尾部
    public void addFooterView(View v) {
        if (!mFooterViews.contains(v)) {
            mFooterViews.add(v);
            notifyDataSetChanged();
        }
    }

    public void removeFooterView(View v) {
        if (mFooterViews.contains(v)) {
            mFooterViews.remove(v);
            notifyDataSetChanged();
        }
    }
}

為了像ListView一樣可以直接調(diào)用addHeaderView肮蛹,我們繼承RecyclerView寫一個WrapperRecyclerView:

public class WrapperRecyclerView extends RecyclerView {

    private WrapperRecyclerAdapter mWrapperRecyclerAdapter;

    public WrapperRecyclerView(Context context) {
        super(context);
    }

    public WrapperRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapperRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        mWrapperRecyclerAdapter = new WrapperRecyclerAdapter(adapter);
        super.setAdapter(mWrapperRecyclerAdapter);
    }

    public void addHeaderView(View view) {
        if (mWrapperRecyclerAdapter != null) {
            mWrapperRecyclerAdapter.addHeaderView(view);
        }
    }

    public void removeHeaderView(View view) {
        if (mWrapperRecyclerAdapter != null) {
            mWrapperRecyclerAdapter.removeHeaderView(view);
        }
    }

    public void addFooterView(View view) {
        if (mWrapperRecyclerAdapter != null) {
            mWrapperRecyclerAdapter.addFooterView(view);
        }
    }

    public void removeFooterView(View view) {
        if (mWrapperRecyclerAdapter != null) {
            mWrapperRecyclerAdapter.removeFooterView(view);
        }
    }
}

MainActivity2類代碼:


public class MainActivity2 extends AppCompatActivity {

    private WrapperRecyclerView recyclerView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview2);

        ArrayList<String> datas = new ArrayList<>();
        for (int i = 0; i < 15; i++) {
            datas.add(" WrapperRecyclerView data " + i);
        }


        recyclerView = findViewById(R.id.recyclerview);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //設(shè)置布局管理器
        recyclerView.setLayoutManager(layoutManager);
        //設(shè)置為垂直布局,這也是默認(rèn)的
        layoutManager.setOrientation(OrientationHelper.VERTICAL);
        //設(shè)置Adapter
        Adapter recycleAdapter = new Adapter(datas);
        //WrapperRecyclerAdapter adapter = new WrapperRecyclerAdapter(recycleAdapter);
        recyclerView.setAdapter(recycleAdapter);

        //View headerView = LayoutInflater.from(this).inflate(R.layout.header_view, null);//todo 這樣寫效果不對
        View headerView = LayoutInflater.from(this).inflate(R.layout.header_view, recyclerView, false);
        View footerView = LayoutInflater.from(this).inflate(R.layout.footer_view, recyclerView, false);

        recyclerView.addHeaderView(headerView);
        recyclerView.addFooterView(footerView);
        //設(shè)置分隔線
        //recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
        //設(shè)置增加或刪除條目的動畫
        recyclerView.setItemAnimator(new DefaultItemAnimator());

    }


}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.ivyzh.designpatterndemo.d1_singleton_pattern.MainActivity">

    <com.ivyzh.designpatterndemo.d4_decorator_pattern.simple2.WrapperRecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

說明:上面的例子有個bug创南,就是點(diǎn)擊item刪除的時候伦忠,會有復(fù)用bug,如下圖:


刪除條目的Bug.png

后面會再排查扰藕。

END.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缓苛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子邓深,更是在濱河造成了極大的恐慌未桥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芥备,死亡現(xiàn)場離奇詭異冬耿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萌壳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門亦镶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袱瓮,你說我怎么就攤上這事缤骨。” “怎么了尺借?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵绊起,是天一觀的道長。 經(jīng)常有香客問我燎斩,道長虱歪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任栅表,我火速辦了婚禮笋鄙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怪瓶。我一直安慰自己萧落,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布洗贰。 她就那樣靜靜地躺著找岖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哆姻。 梳的紋絲不亂的頭發(fā)上宣增,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天玫膀,我揣著相機(jī)與錄音矛缨,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛箕昭,可吹牛的內(nèi)容都是我干的灵妨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼落竹,長吁一口氣:“原來是場噩夢啊……” “哼泌霍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起述召,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤朱转,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后积暖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藤为,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年夺刑,在試婚紗的時候發(fā)現(xiàn)自己被綠了缅疟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡遍愿,死狀恐怖存淫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沼填,我是刑警寧澤桅咆,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站倾哺,受9級特大地震影響轧邪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羞海,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一忌愚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧却邓,春花似錦硕糊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撬腾,卻和暖如春螟蝙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背民傻。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工胰默, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留场斑,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓牵署,卻偏偏與公主長得像漏隐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子奴迅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,114評論 25 707
  • 用兩張圖告訴你青责,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,723評論 2 59
  • 設(shè)計(jì)模式概述 在學(xué)習(xí)面向?qū)ο笃叽笤O(shè)計(jì)原則時需要注意以下幾點(diǎn):a) 高內(nèi)聚取具、低耦合和單一職能的“沖突”實(shí)際上脖隶,這兩者...
    彥幀閱讀 3,743評論 0 14
  • 最大公約數(shù)[1] ①定義 幾個自然數(shù)公有的約數(shù),叫做這幾個數(shù)的公約數(shù)暇检;其中最大的一個浩村,叫做這幾個數(shù)的最大公約數(shù)。 ...
    dreamsfuture閱讀 4,024評論 0 1
  • 追一場春光 追一個最及時的夢
    檸檸檸萌閱讀 97評論 0 0