RecyclerView系列之二:添加分隔線

在上一篇RecyclerView系列之一:實(shí)現(xiàn)常見(jiàn)的ListView效果簡(jiǎn)單介紹了使用RecyclerView如何實(shí)現(xiàn)ListView的效果瓶珊,但是我們也發(fā)現(xiàn)了,效果圖中沒(méi)有分隔線,今天將介紹如何為RecyclerView添加分隔線.

一、如何添加分隔線

嗯佃乘,首先有兩種比較low的方式:
1、為item布局設(shè)置一個(gè)背景色驹尼,再為item根標(biāo)簽設(shè)置一個(gè)margin或者padding趣避,這樣就形成了分隔線的效果.
2、在Item布局文件最后加一條橫線新翎,為它設(shè)置一個(gè)背景色程帕,形成分隔線的效果.

然后就是正式的寫(xiě)法了:
3、RecyclerView中可以通過(guò)addItemDecoration()方法添加分割線地啰, 該方法的參數(shù)為RecyclerView.ItemDecoration愁拭,該類為抽象類,官方目前只提供了一個(gè)實(shí)現(xiàn)類DividerItemDecoration.

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private LinearLayoutManager layoutManager;
    private MainAdapter mAdapter;
    private List<String> mDatas;
    private DividerItemDecoration mDivider;//分隔線

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

        initData();
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        mRecyclerView.setLayoutManager(layoutManager);

        //初始化分隔線髓绽、添加分隔線
        mDivider = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mDivider);

        mAdapter = new MainAdapter(this,mDatas);
        mRecyclerView.setAdapter(mAdapter);
    }

    private void initData() {
        mDatas = new ArrayList<>();
        for (int i = 'A'; i < 'Z'; i++) {
            mDatas.add("" + (char) i);
        }
    }
}

現(xiàn)在我們?cè)賮?lái)看一下效果圖:


看起來(lái)還行敛苇,但是萬(wàn)一這種效果不是我們需要的怎么辦?所以我們應(yīng)該知道RecyclerView是如何畫(huà)出分隔線的

二顺呕、抽象類RecyclerView.ItemDecoration源碼

 public static abstract class ItemDecoration {
       
        public void onDraw(Canvas c, RecyclerView parent, State state) {
            onDraw(c, parent);
        }
        @Deprecated
        public void onDraw(Canvas c, RecyclerView parent) {
        }

        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
            onDrawOver(c, parent);
        }
        @Deprecated
        public void onDrawOver(Canvas c, RecyclerView parent) {
        }


        @Deprecated
        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
            outRect.set(0, 0, 0, 0);
        }
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }
    }

當(dāng)我們調(diào)用addItemDecoration()方法添加decoration的時(shí)候枫攀,RecyclerView就會(huì)調(diào)用該類的onDraw方法去繪制分隔線,也就是說(shuō):分隔線是繪制出來(lái)的. 下面來(lái)了解分隔線是如何繪制出來(lái)的.

三株茶、理解分隔線是如何繪制的

系統(tǒng)默認(rèn)實(shí)現(xiàn)類DividerItemDecoration涉及到clipToPadding屬性来涨、畫(huà)布的裁剪一些知識(shí),不太容易理解启盛,也不方便修改蹦掐,這里介紹網(wǎng)上通用的實(shí)現(xiàn).
首先要理解一個(gè)概念:分隔線本質(zhì)是一個(gè)矩形,這個(gè)“線”是有長(zhǎng)度和寬度的.

/**
* 默認(rèn)分隔線實(shí)現(xiàn)類只支持布局管理器為 LinearLayoutManager
*/
public class CommonItemDecoration extends RecyclerView.ItemDecoration {
    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;

    //使用系統(tǒng)主題中的R.attr.listDivider作為Item間的分割線
    private static final int[] ATTRS = new int[]{ android.R.attr.listDivider};

    private Drawable mDivider;

    private int mOrientation;//布局方向僵闯,決定繪制水平分隔線還是豎直分隔線

    private final Rect mBounds = new Rect();


    public CommonItemDecoration (Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException(
                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
    }

    /**
     * 一個(gè)app中分隔線不可能完全一樣卧抗,你可以通過(guò)這個(gè)方法傳遞一個(gè)Drawable 對(duì)象來(lái)定制分隔線
     */
    public void setDrawable(@NonNull Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable cannot be null.");
        }
        mDivider = drawable;
    }

    /**
     * 畫(huà)分隔線
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getLayoutManager() == null) {
            return;
        }
        if (mOrientation == VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    /**
     * 在LinearLayoutManager方向?yàn)閂ertical時(shí),畫(huà)分隔線
     */
    public void drawVertical(Canvas canvas, RecyclerView parent) {
        final int left = parent.getPaddingLeft();//★分隔線的左邊 = paddingLeft值
        final int right = parent.getWidth() - parent.getPaddingRight();//★分隔線的右邊 = RecyclerView 寬度-paddingRight值
//分隔線不在RecyclerView的padding那一部分繪制

        final int childCount = parent.getChildCount();//★分隔線數(shù)量=item數(shù)量
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);//確定是第幾個(gè)item
            final RecyclerView.LayoutParams params =(RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;//★分隔線的上邊 = item的底部 + item根標(biāo)簽的bottomMargin值 
            final int bottom = top + mDivider.getIntrinsicHeight();//★分隔線的下邊 = 分隔線的上邊 + 分隔線本身高度
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
    }


    /**
     * 在LinearLayoutManager方向?yàn)镠orizontal時(shí)鳖粟,畫(huà)分隔線
     *      理解了上面drawVertical()方法這個(gè)方法也就理解了
     */
    public void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
    }

    /**
     *  獲取Item偏移量
     *    此方法是為每個(gè)Item四周預(yù)留出空間社裆,從而讓分隔線的繪制在預(yù)留的空間內(nèi)
     */
   @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
        if (mOrientation == VERTICAL) {//豎直方向的分隔線:item向下偏移一個(gè)分隔線的高度
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {//水平方向的分隔線:item向右偏移一個(gè)分隔線的寬度
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

四、更改分隔線的樣式

DividerItemDecoration畫(huà)的分割線是讀取系統(tǒng)的屬性android.R.attr.listDivider向图,使用系統(tǒng)的listDivider好處就是就是方便我們?nèi)ルS意的分隔線的樣式

1泳秀、找到res/values/styles.xml,在其中聲明android:listDivider屬性,然后使用我們自己的樣式
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:listDivider">@drawable/my_divider</item>
    </style>
</resources>
2榄攀、在res/drawable目錄下聲明我們自己的樣式my_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <gradient
        android:centerColor="#ff00ff"
        android:endColor="#00ff00"
        android:startColor="#0000ff"
        android:type="linear" />
    <size android:height="4dp"/>
</shape>

再來(lái)看一下效果圖


3嗜傅、當(dāng)然,這樣一修改就改變整個(gè)app中的分隔線效果了檩赢,如果只是想改變某個(gè)列表中的分隔線效果吕嘀,完全可以通過(guò)分隔線的setDrawable方法來(lái)修改
mDivider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
mDivider.setDrawable(getResources().getDrawable(R.drawable.my_divider));
mRecyclerView.addItemDecoration(mDivider);

這樣一來(lái),不同的列表就可以使用不同的分隔線效果了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市币他,隨后出現(xiàn)的幾起案子坞靶,更是在濱河造成了極大的恐慌,老刑警劉巖蝴悉,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彰阴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拍冠,警方通過(guò)查閱死者的電腦和手機(jī)尿这,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庆杜,“玉大人射众,你說(shuō)我怎么就攤上這事』尾疲” “怎么了叨橱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)断盛。 經(jīng)常有香客問(wèn)我罗洗,道長(zhǎng),這世上最難降的妖魔是什么钢猛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任伙菜,我火速辦了婚禮,結(jié)果婚禮上命迈,老公的妹妹穿的比我還像新娘贩绕。我一直安慰自己,他們只是感情好壶愤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布淑倾。 她就那樣靜靜地躺著,像睡著了一般征椒。 火紅的嫁衣襯著肌膚如雪踊淳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天陕靠,我揣著相機(jī)與錄音,去河邊找鬼脱茉。 笑死剪芥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的琴许。 我是一名探鬼主播税肪,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了益兄?” 一聲冷哼從身側(cè)響起锻梳,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎净捅,沒(méi)想到半個(gè)月后疑枯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛔六,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年荆永,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片国章。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡具钥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出液兽,到底是詐尸還是另有隱情骂删,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布四啰,位于F島的核電站宁玫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拟逮。R本人自食惡果不足惜撬统,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敦迄。 院中可真熱鬧恋追,春花似錦、人聲如沸罚屋。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脾猛。三九已至撕彤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猛拴,已是汗流浹背羹铅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愉昆,地道東北人职员。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跛溉,于是被迫代替她去往敵國(guó)和親焊切。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扮授,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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