AndroidUI初探③RecyclerView之ItemDecoration

前言

今年換了新工作 , 剛開始熟悉項目 卒煞,今天終于交付了一個版本 , 整個人忙得暈頭轉(zhuǎn)向 叼架, 每次接手新項目 跷坝,都需要一段時間去熟悉項目 , 有時候會因為不熟悉前任寫的代碼 碉碉, 可能會掉進(jìn)不明不白的坑里 柴钻, 至此,我覺得想要最快熟悉前任寫代碼 垢粮,當(dāng)前又沒有任務(wù) 贴届,可以小范圍重構(gòu) , 并進(jìn)行測試 蜡吧, 可以減少因為不熟悉項目的一些細(xì)節(jié) 毫蚓,而踩到不明不白的坑 。

ItemDecoration

使用過RecyclerView的朋友肯定有過這樣的經(jīng)歷 昔善, 以前在使用ListView的時候元潘,在Item之間的分割線上 , 使用ListView的divider屬性就可以搞定君仆,但在RecyclerView中卻再也沒見到這個屬性翩概,那么他到哪里去了呢 ? RecyclerView給我們提供了這樣的方法addItemDecoration(ItemDecoration decor) 傳遞進(jìn)去的ItemDecoration對象就是今天的主角返咱,專門用于處理Item之間的間隔和拓展 钥庇。

RecyclerView源碼中可以看到,ItemDecoration是其子類并且還是一個抽象類咖摹,里面有幾個抽象方法评姨,需要我們繼承ItemDecoration,并進(jìn)行重寫的 萤晴。

/**
     * 獲得條目偏移量 吐句, 回調(diào)此方法來確定每個Item的偏移量
     * @param outRect 矩形區(qū)域 , 每一個Item都是一個矩形區(qū)域胁后,rect表示矩形區(qū)域的四個方位
     * @param view 每個條目的View對象
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

    }

    /**
     * 間隔線繪制
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
      
    }

我們從源碼中可以看到 , ItemDecoration里面嗦枢,全是RecyclerView在內(nèi)部會回調(diào)的方法择同,并提供onDraw方法 ,這樣給了我們極大的自定義性净宵,可以畫出任意圖案的Item 。

自定義簡單的ItemDecoration

移動設(shè)備開發(fā)裹纳,大部分時間都是在可以List列表類打交道择葡,因為手持設(shè)備的顯示空間有限,可以只能通過上下滑動來展示更多的內(nèi)容剃氧。因為RecyclerView沒有給我們提供常規(guī)的divider敏储,那么我們就指定一個簡單的ItemDecoration,了解它自定義的原理 朋鞍。只有了解了其原理 已添, 以后想要什么樣的都有 。

ItemDecoration給我們提供了兩個方法滥酥,onDraw方法自不必多說 更舞, 大家都知道 。getItemOffsets這個方法估計就比較少見了坎吻, 這個方法是設(shè)置我們每個Item的偏移量缆蝉,也就是我們需要Item給出多大的繪制空間 , 這個方法給了Rect對象瘦真,用來設(shè)置巨型的偏移量刊头,也就是每個Item多出多燒來進(jìn)行繪制你給的圖案 。

getItemOffsets 圖解

rect 偏移量圖解

由圖上可以看出 诸尽, 每個Item都是一個rect 原杂, 而我們要繪制的就是rect的四個邊, 上圖是以畫類似ListView的divider為例子 您机。

畫水平線

畫水平線 穿肄, 想要畫水平線,就要先弄清楚 际看, 要畫rect的哪個邊被碗,就List的來說,是rect的下邊也就是bottom 仿村。在getItemOffsets設(shè)置rect的下邊的偏移量 锐朴。

設(shè)置偏移量

outRect.set(0,0,0,drawable.getIntrinsicHeight()); // 將drawable繪制在Item的底部

這樣就在rect的底部 , 偏移了一定的高度蔼囊,可以繪制并顯示我們的圖案 焚志。

繪制

/**
     * 畫水平線
     * @param c 畫布
     * @param parent
     */
    private void drawHorizontalLine(Canvas c, RecyclerView parent) {

        int left = parent.getPaddingLeft(); // 從recyclerView容器內(nèi)邊距算起 衣迷, 左邊起點坐標(biāo)
        int right = parent.getWidth() - parent.getPaddingRight(); // recyclerView寬度減去paddingRight計算右邊坐標(biāo)
        // 以上 , Item decoration的寬度坐標(biāo)確定 , 即rect的寬度

        int childCount = parent.getChildCount();
        for (int i = 0; i <childCount ; i++) {
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bottom = top + drawable.getIntrinsicHeight();
            // 每一個Item都需要繪制一遍
            drawable.setBounds(left,top,right,bottom);
            drawable.draw(c);
        }
    }

因為繪制的是Drawable所以也需要指定drawable的rect 酱酬, 指定rect的方法為drawable.setBounds(left,top,right,bottom);壶谒,這樣來確定drawable繪制的圖形大小寬高 。

畫水平線膳沽,所有rect的左邊是從RecyclerView的左內(nèi)邊距算起(內(nèi)邊距不參與繪制) 汗菜, 右邊就是RecyclerView的整個寬度減去右內(nèi)邊距,這樣計算得出Item的寬度 挑社。因為是ListView陨界,每條線都在不同的Item下,所有rect的上邊就在每個Item的bottom下面痛阻,所有drawable的上邊就要從每個Item的下邊算起菌瘪,即 childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView)); 而drawable rect的下邊則是每個Item的bottom 加上 drawable的高度 , 這樣drawable rect的四個邊就計算完成了 阱当, 我們水平線也就畫出來了 俏扩。

完整源碼

public class SimpleItemDecoration extends RecyclerView.ItemDecoration {

    private int orientation;

    private Drawable drawable;

    public SimpleItemDecoration(@NonNull int orientation, @NonNull Drawable drawable) {
        this.orientation = orientation;
        this.drawable = drawable;
    }

    /**
     * 設(shè)置RecyclerView的布局管理器
     * @param orientation
     */
    public void setOrientation(int orientation) {
        if (LinearLayoutManager.HORIZONTAL != orientation && LinearLayoutManager.VERTICAL != orientation)
            new IllegalArgumentException("RecyclerView不支持此布局管理器");

        this.orientation = orientation;
    }

    /**
     * 獲得條目偏移量 , 回調(diào)此方法來確定每個Item的偏移量
     * @param outRect 矩形區(qū)域 , 每一個Item都是一個矩形區(qū)域弊添,rect表示矩形區(qū)域的四個方位
     * @param view 每個條目的View對象
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (LinearLayoutManager.VERTICAL == orientation) {
            outRect.set(0,0,0,drawable.getIntrinsicHeight()); // 將drawable繪制在Item的底部
        }else if (LinearLayoutManager.HORIZONTAL == orientation) {
            outRect.set(0,0,drawable.getIntrinsicWidth(),0); // 將drawable繪制在Item的右邊
        }
    }

    /**
     * 間隔線繪制
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (LinearLayoutManager.VERTICAL == orientation) {
            drawHorizontalLine(c,parent); //畫水平線
        }else if (LinearLayoutManager.HORIZONTAL == orientation) {
            drawVerticalLine(c,parent); // 畫垂直線
        }
    }

    /**
     * 畫水平線
     * @param c 畫布
     * @param parent
     */
    private void drawHorizontalLine(Canvas c, RecyclerView parent) {

        int left = parent.getPaddingLeft(); // 從recyclerView容器內(nèi)邊距算起 录淡, 左邊起點坐標(biāo)
        int right = parent.getWidth() - parent.getPaddingRight(); // recyclerView寬度減去paddingRight計算右邊坐標(biāo)
        // 以上 , Item decoration的寬度坐標(biāo)確定 , 即rect的長度

        int childCount = parent.getChildCount();
        for (int i = 0; i <childCount ; i++) {
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bottom = top + drawable.getIntrinsicHeight();
            // 每一個Item都需要繪制一遍
            drawable.setBounds(left,top,right,bottom);
            drawable.draw(c);
        }
    }

    /**
     * 畫垂直線
     * @param canvas
     * @param parent
     */
    private void drawVerticalLine(Canvas canvas,RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0; i <childCount ; i++) {

            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + drawable.getIntrinsicWidth();

            drawable.setBounds(left,top,right,bottom);
            drawable.draw(canvas);
        }
    }
}

結(jié)語

RecyclerView很強(qiáng)大 油坝, 但是需要自定義的很多 赁咙, 這也就造成了RecyclerView可以寫出千變?nèi)f化的UI出來,如此松耦合的庫免钻,需要我們不斷的去學(xué)習(xí) 彼水。

很久沒寫文章了 , 今年忙碌并且充實著极舔,也即將迎來人生中的第一輛車凤覆,想想渾身都是動力 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拆魏,一起剝皮案震驚了整個濱河市盯桦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渤刃,老刑警劉巖拥峦,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卖子,居然都是意外死亡略号,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玄柠,“玉大人突梦,你說我怎么就攤上這事∮鹄” “怎么了宫患?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長这弧。 經(jīng)常有香客問我娃闲,道長,這世上最難降的妖魔是什么匾浪? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任皇帮,我火速辦了婚禮,結(jié)果婚禮上户矢,老公的妹妹穿的比我還像新娘。我一直安慰自己殉疼,他們只是感情好梯浪,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓢娜,像睡著了一般挂洛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眠砾,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天虏劲,我揣著相機(jī)與錄音,去河邊找鬼褒颈。 笑死柒巫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谷丸。 我是一名探鬼主播堡掏,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼刨疼!你這毒婦竟也來了泉唁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揩慕,失蹤者是張志新(化名)和其女友劉穎亭畜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迎卤,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡拴鸵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宝踪。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡侨糟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘩燥,到底是詐尸還是另有隱情秕重,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布厉膀,位于F島的核電站溶耘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏服鹅。R本人自食惡果不足惜凳兵,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望企软。 院中可真熱鬧庐扫,春花似錦、人聲如沸仗哨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厌漂。三九已至萨醒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苇倡,已是汗流浹背富纸。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留旨椒,地道東北人晓褪。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像综慎,于是被迫代替她去往敵國和親辞州。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

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