裝飾設計模式 - RecyclerView添加頭部和底部

1.問題思考


RecyclerView 我們都知道有一個比較麻煩的事酪捡,那就是沒有提供可以直接添加底部和頭部的功能。而在開發(fā)的過程中一定有這方面的需求全闷,怎么解決這個問題?我們干脆不要用 RecyclerView 了潘鲫,直接用 ListView 就好了翁逞,因為 ListView 直接提供了 addHeaderView 和 addFooterView 方法。既然 ListView 可以直接可以添加頭部和底部溉仑,那么是不是可以去看看它的源碼挖函,我們依葫蘆畫瓢用到 RecyclerView 中不就好了。還有就是前面反復提到的浊竟,這個時候可以腦海中想想怨喘,是不是可以用設計模式去解決這個問題?

接下來看下裝飾設計模式振定,我們的設計模式文章很少寫到一些生活小事例的代碼必怜, 如果實在看不懂,可以去看看其他的一些文章后频,寫的都還蠻不錯的梳庆,那些代碼可以加深我們的印象,這里我們主要以開發(fā)中實際案例為主徘郭。

2. 裝飾設計模式


2.1 模式的定義

裝飾設計模式也稱包裝設計模式靠益,使用一種透明的方式來動態(tài)的擴展對象的功能,也是繼承關系的的一種替代方案之一残揉。說個大白話就是,在不使用的繼承的方式下芋浮,采用裝飾設計模式可以擴展一個對象的功能抱环,可以使一個對象變得越來越強大。

2.2 模式的運用

RecyclerView 本身是不支持添加底部和頭部的纸巷,那么采用裝飾設計模式可以對其進行功能擴展镇草,使其能夠支持底部和頭部的添加:

/**
 * description: 可以添加頭部底部的 WrapRecyclerAdapter
 * author: Darren on 2017/9/25 09:54
 * email: 240336124@qq.com
 * version: 1.0
 */
class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    // 包裝 adapter 是原來的 RecyclerView.Adapter 是并不支持添加頭部和底部的
    private RecyclerView.Adapter mRealAdapter;
    ArrayList<View> mHeaderViews; // 頭部
    ArrayList<View> mFooterViews; // 底部
    
    public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
        mRealAdapter = adapter;
        mHeaderViews = new ArrayList<>();
        mFooterViews = new ArrayList<>();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();

        if (position < numHeaders) {
            return createFooterHeaderViewHolder(mHeaderViews.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 createFooterHeaderViewHolder(mFooterViews.get(adjPosition - adapterCount));
    }

    public int getHeadersCount() {
        return mHeaderViews.size();
    }

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

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return;
        }
        // Adapter
        final int adjPosition = position - numHeaders;
        if (mRealAdapter != null) {
            int adapterCount = mRealAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mRealAdapter.onBindViewHolder(holder, adjPosition);
            }
        }
    }

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

    /**
     * 添加底部View
     * @param view
     */
    public void addFooterView(View view) {
        if (!mFooterViews.contains(view)) {
            mFooterViews.add(view);
            notifyDataSetChanged();
        }
    }

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

    /**
     * 移除底部View
     * @param view
     */
    public void removeFooterView(View view) {
        if (mFooterViews.contains(view)) {
            mFooterViews.remove(view);
            notifyDataSetChanged();
        }
    }

    /**
     * 移除頭部View
     * @param view
     */
    public void removeHeaderView(View view) {
        if (mHeaderViews.contains(view)) {
            mHeaderViews.remove(view);
            notifyDataSetChanged();
        }
    }

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

}
/**
 * description: 支持添加底部和頭部的 RecyclerView
 * author: Darren on 2017/9/27 10:07
 * email: 240336124@qq.com
 * version: 1.0
 */
public class WrapRecyclerView extends RecyclerView{
    // 支持添加頭部和底部的 RecyclerView.Adapter
    private WrapRecyclerAdapter mWrapAdapter;

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

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

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

    @Override
    public void setAdapter(Adapter adapter) {
        // 這里做一個替換
        mWrapAdapter = new WrapRecyclerAdapter(adapter);
        super.setAdapter(mWrapAdapter);
    }

    /**
     * 添加頭部View
     * @param view
     */
    public void addHeaderView(View view){
        if(mWrapAdapter != null){
            mWrapAdapter.addHeaderView(view);
        }
    }

    /**
     * 添加底部View
     * @param view
     */
    public void addFooterView(View view){
        if(mWrapAdapter != null){
            mWrapAdapter.addFooterView(view);
        }
    }

    /**
     * 移除頭部View
     * @param view
     */
    public void removeHeaderView(View view){
        if(mWrapAdapter != null){
            mWrapAdapter.removeHeaderView(view);
        }
    }

    /**
     * 移除底部View
     * @param view
     */
    public void removeFooterView(View view){
        if(mWrapAdapter != null){
            mWrapAdapter.removeFooterView(view);
        }
    }
}
// 實例化頭部View
View headerView = LayoutInflater.from(this).inflate(R.layout.layout_rc_header, mRecyclerView, false);
// 設置適配器
mRecyclerView.setAdapter(new RecyclerViewAdapter());
// 添加頭部
mRecyclerView.addHeaderView(headerView);

所有分享大綱:Android進階之旅 - 系統(tǒng)架構篇

視頻講解地址:http://pan.baidu.com/s/1nvKFHK9

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瘤旨,隨后出現(xiàn)的幾起案子梯啤,更是在濱河造成了極大的恐慌,老刑警劉巖存哲,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因宇,死亡現(xiàn)場離奇詭異,居然都是意外死亡祟偷,警方通過查閱死者的電腦和手機察滑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來修肠,“玉大人贺辰,你說我怎么就攤上這事。” “怎么了饲化?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵莽鸭,是天一觀的道長。 經(jīng)常有香客問我吃靠,道長硫眨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任撩笆,我火速辦了婚禮捺球,結果婚禮上,老公的妹妹穿的比我還像新娘夕冲。我一直安慰自己氮兵,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布歹鱼。 她就那樣靜靜地躺著泣栈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弥姻。 梳的紋絲不亂的頭發(fā)上南片,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音庭敦,去河邊找鬼疼进。 笑死,一個胖子當著我的面吹牛秧廉,可吹牛的內(nèi)容都是我干的伞广。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疼电,長吁一口氣:“原來是場噩夢啊……” “哼嚼锄!你這毒婦竟也來了?” 一聲冷哼從身側響起蔽豺,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤区丑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后修陡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沧侥,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年濒析,在試婚紗的時候發(fā)現(xiàn)自己被綠了正什。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡号杏,死狀恐怖婴氮,靈堂內(nèi)的尸體忽然破棺而出斯棒,到底是詐尸還是另有隱情,我是刑警寧澤主经,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布荣暮,位于F島的核電站,受9級特大地震影響罩驻,放射性物質(zhì)發(fā)生泄漏穗酥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一惠遏、第九天 我趴在偏房一處隱蔽的房頂上張望砾跃。 院中可真熱鬧,春花似錦节吮、人聲如沸抽高。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翘骂。三九已至,卻和暖如春帚豪,著一層夾襖步出監(jiān)牢的瞬間碳竟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工狸臣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莹桅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓烛亦,卻偏偏與公主長得像统翩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子此洲,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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