自定義ItemDecoration分割線的高度、顏色、偏移,看完這個你就懂了

想到分割線,原先一直是在item的布局中直接加入,在adapter中進行判斷,若是最后一個子項則將分割線隱藏,感覺太小兒科了,今天來好好研究這個ItemDecoration的使用。本來周末要寫這篇文章的,但奈何只有單休,一偷懶就沒寫,今天給補上,畢業(yè)工作半年后的第一篇筆記,一定詳細,不對的地方望指教,輕點打我...

文章參考自RecyclerView 之 ItemDecoration 講解及高級特性實踐,寫的很詳細,仔細看后就會用了,我只是在此基礎(chǔ)上增添了可以更改顏色、寬度、左右偏移的功能。廢話不多說,咱們開始做吧。

簡單的添加分割線:

一、建立工程,創(chuàng)建Adapter,加載布局文件

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.recyclerview)
    RecyclerView recyclerview;          
    private List<String> dataList;      //數(shù)據(jù)項
    private MyAdapter myAdapter;        //適配器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        initData();

        myAdapter = new MyAdapter(R.layout.item_recyclerview,dataList);
        recyclerview.setAdapter(myAdapter);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerview.setLayoutManager(layoutManager);
    }
    private void initData(){
        dataList = new ArrayList<>();
        for(int i = 0;i<20;i++){
            dataList.add("子項"+i);
        }
    }
}
public class MyAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
    public MyAdapter(int layoutResId, List<String> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, String item) {
        helper.setText(R.id.tv_content, item);
    }
}
1

二、建立分割器

提前說明,以下內(nèi)容均是在每個ItemView的頂部加入分割線,第一個不加

通過recyclerview.addItemDecoration(new SimpleItemDecoration());將以下分割器加入到RecyclerView中即可

public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
    /**
     * @param outRect   全為0的rect,用來指定偏移區(qū)域
     * @param view      指RecyclerView中的Item
     * @param parent    指RecyclerView本身
     * @param state     狀態(tài)
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        if (parent.getChildAdapterPosition(view) != 0) {
            //直接設(shè)置為1px
            outRect.top = 1;
        }
    }
}

加入后的效果:


2

自定義顏色偏移寬度的分割器

以上方法,通過使每個ItemView向上撐出1px距離,而RecyclerView背景為灰色,這樣就顯示出1px的灰色線,實現(xiàn)分割線功能,看到這你可能會想,這也太粗糙了,如果我想要改變分割線的寬度、顏色該怎么辦,總不能每寫一個RecyclerView都再寫一套分割器,更改RecyclerView背景顏色吧,而且一般分割線并不占滿全部寬度,有左右偏移,那該怎么實現(xiàn)呢?

別急,我們先了解下getItemOffsets()方法中的outRect這個參數(shù)。

3

其中的藍色部分為我們的RecyclerView的子項ItemView,外部黃色部分為outRect,只是黃色,并不包含ItemView壓的那部分,left,right,top,bottom四個參數(shù)其實就是距離itemView的四個方向的偏移量,是指偏移 ItemView 各個方向的數(shù)值,在上面的例子中,我們設(shè)置了outRect.top=1,所以每個ItemView之間有1px的空隙,所以呈現(xiàn)出1px灰色的分割線,分割線顏色決定于RecyclerView的背景色。

一、設(shè)置高度:

既然知道了這四個參數(shù)代表相對itemview的偏移,那么分割線的高度就好辦了。

4

如圖,想要紅色那樣高度的分割線,只需要outRect.top等于該高度就可以了。我們將該高度定義為mDividerHeight

二、設(shè)置顏色、左右偏移:

高度有了,如果我們只想繪制紅色那部分矩形而不是ItemView上方的全部該怎么辦?我們知道每一個View中的onDraw()方法是用來繪制組件的UI效果,所以想要顏色的話,需要我們重寫ItemDecoration中的onDraw()方法。

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);

可以看到onDraw()方法中有參數(shù)Canvas,通過它來繪制紅色矩形,所以我們需要知道該矩形的四條邊的位置。

float dividerTop = view.getTop() - mDividerHeight;                          //矩形頂部
float dividerBottom = view.getTop();                                       //矩形底部
float dividerLeft = parent.getPaddingLeft() + margin;                         //矩形左側(cè)         
float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;   //矩形右側(cè)

矩形頂部=itemview的頂部加上分割線的高度,咦?我怎么寫的減號?看下圖你應(yīng)該就會明白

5

安卓中坐標是這樣的,向下向右為正,所以紅色矩形頂部位置就應(yīng)該是itemView的top位置-矩形高度

偏移的話左側(cè)加上偏移量,右側(cè)減去偏移量即可。

c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);

這樣我們要繪制的矩形就出來了,等等,我們只是畫了個矩形,還沒顏色呢,再來看看drawRect()中的參數(shù),我們還缺一個mPaint畫筆,通過它來設(shè)置矩形分割線顏色。

public MyDecoration() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);          //抗鋸齒
        mPaint.setColor(Color.GRAY);        //默認灰色
}

通過以上步驟,帶有顏色和偏移量,且具有一定高度的分割線就畫好了,其實還沒完,需要注意:getItemOffsets 是針對每一個 ItemView,而 onDraw 方法卻是針對 RecyclerView 本身,所以在 onDraw 方法中需要遍歷屏幕上可見的 ItemView,分別獲取它們的位置信息,然后分別的繪制對應(yīng)的分割線。

代碼如下:

  @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();    //可見ItemView個數(shù)
//因為getItemOffsets是針對每一個ItemView,而onDraw方法是針對RecyclerView本身,所以需要循環(huán)遍歷來設(shè)置
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            //第一個ItemView不需要繪制
            if (index == 0) {
                continue;//跳過本次循環(huán)體中尚未執(zhí)行的語句,立即進行下一次的循環(huán)條件判斷
            }
          float dividerTop = view.getTop() - mDividerHeight;                    //矩形頂部    
          float dividerLeft = parent.getPaddingLeft() + margin;                 //矩形左側(cè)       
          float dividerBottom = view.getTop();                                  //矩形底部
          float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;//矩形右側(cè) 
          c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
        }
    }

在實際運用中,我們的分割線顏色高度等樣式可能不一樣,這里我們通過建造者模式來設(shè)置這些屬性

//設(shè)置左右偏移(默認是設(shè)置的一樣的,若需要自己更改)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }
    //設(shè)置顏色
    public MyDecoration setColor(int color) {
        mPaint.setColor(color);
        return this;
    }
    //設(shè)置分割線高度
    public MyDecoration setDividerHeight(float height) {
        this.mDividerHeight = height;
        return this;
    }

這樣我們就完成了分割線的自定義

完整代碼如下:

public class MyDecoration extends RecyclerView.ItemDecoration {

    private float mDividerHeight = 1; //線的高度
    private Paint mPaint;           //畫筆將自己做出來的分割線矩形畫出顏色
    private float margin = 0;       //左右偏移量

    public MyDecoration() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);          //抗鋸齒
        mPaint.setColor(Color.GRAY);        //默認顏色
    }

    //通過建造者模式來設(shè)置三個屬性
    //設(shè)置左右偏移(默認是設(shè)置的一樣的,若需要自己更改)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }

    //設(shè)置顏色
    public MyDecoration setColor(int color) {
        mPaint.setColor(color);
        return this;
    }

    //設(shè)置分割線高度
    public MyDecoration setDividerHeight(float height) {
        this.mDividerHeight = height;
        return this;
    }

    //在這里就已經(jīng)把寬度的偏移給做好了
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        //第一個ItemView不需要在上面繪制分割線
        if (parent.getChildAdapterPosition(view) != 0) {
            
            outRect.top = (int) mDividerHeight;//指相對itemView頂部的偏移量
        }
    }
    //這里主要是繪制顏色的
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();
//因為getItemOffsets是針對每一個ItemView,而onDraw方法是針對RecyclerView本身,所以需要循環(huán)遍歷來設(shè)置
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            //第一個ItemView不需要繪制
            if (index == 0) {
                continue;//跳過本次循環(huán)體中尚未執(zhí)行的語句,立即進行下一次的循環(huán)條件判斷
            }
            float dividerTop = view.getTop() - mDividerHeight;                                  
            float dividerLeft = parent.getPaddingLeft() + margin;                               
            float dividerBottom = view.getTop();
            float dividerRight = parent.getWidth() - parent.getPaddingRight() - margin;         
            c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
        }
    }
}

使用:

MyDecoration myDecoration = new MyDecoration();

myDecoration.setColor(ContextCompat.getColor(getContext(),R.color.line_gray)).setMargin(ConvertUtils.dp2px(getContext(), 15)).setDividerHeight(ConvertUtils.dp2px(getContext(),1));

recyclerView.addItemDecoration(myDecoration);

實際使用中我們是dp單位,所以這里我使用了ConvertUtils工具類,將dp轉(zhuǎn)為px

代碼如下:

public static int dp2px(Context context, final float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

看下效果:

6

到這里我們想要的功能就全部完成了。(終于碼完了開心!)

如果我們的RecyclerView是橫向的滑動,原理類似,剩下的就交給你們了(懶得寫了嘿嘿)

GitHub地址

總結(jié)一下

一共兩步:

1、通過getItemOffsets()在itemView頂部撐出來一片區(qū)域

2、通過onDraw()方法來在該區(qū)域內(nèi)繪制想要顏色及偏移量的分割線

其實ItemDecoration還有很多很牛逼的地方,例如實現(xiàn)時光軸效果,排行榜的角標,可以看看我參考的那篇文章的實現(xiàn),寫得很詳細的,是真大佬!如果想看我寫的話,就等我哪天做出來了再來更新吧(你們催我的話就那我就只好趕緊抓緊實現(xiàn)下咯)

第一篇文章就這么多,有錯誤的地方還望指出,畢竟工作半年,經(jīng)驗少的很......如果對你有幫助記得點贊哦。寫完這篇文章感覺收獲多多,其實我還定了很多文章要寫,看來要通過寫文章來改變自己懶的毛病了嘿嘿

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容