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

想到分割線,原先一直是在item的佈局中直接加入,在adapter中進行判斷,如果最後一個子項則將分割線隱藏,感受過小兒科了,今天來好好研究這個ItemDecoration的使用。java

文章參考自RecyclerView 之 ItemDecoration 講解及高級特性實踐,寫的很詳細,仔細看後就會用了,我只是在此基礎上增添了能夠更改顏色、寬度、左右偏移的功能。廢話很少說,我們開始作吧。git

簡單的添加分割線:

1、創建工程,建立Adapter,加載佈局文件github

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.recyclerview)
    RecyclerView recyclerview;			
    private List<String> dataList;		//數據項
    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

2、創建分割器ide

提早說明,如下內容均是在每一個ItemView的頂部加入分割線,第一個不加工具

經過recyclerview.addItemDecoration(new SimpleItemDecoration());將如下分割器加入到RecyclerView中便可佈局

public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
    /** * @param outRect 全爲0的rect,用來指定偏移區域 * @param view 指RecyclerView中的Item * @param parent 指RecyclerView自己 * @param state 狀態 */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        if (parent.getChildAdapterPosition(view) != 0) {
            //直接設置爲1px
            outRect.top = 1;
        }
    }
}
複製代碼

加入後的效果: ui

2

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

以上方法,經過使每一個ItemView向上撐出1px距離,而RecyclerView背景爲灰色,這樣就顯示出1px的灰色線,實現分割線功能,看到這你可能會想,這也太粗糙了,若是我想要改變分割線的寬度、顏色該怎麼辦,總不能每寫一個RecyclerView都再寫一套分割器,更改RecyclerView背景顏色吧,並且通常分割線並不佔滿所有寬度,有左右偏移,那該怎麼實現呢?this

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

3

其中的藍色部分爲咱們的RecyclerView的子項ItemView,外部黃色部分爲outRect,只是黃色,並不包含ItemView壓的那部分,left,right,top,bottom四個參數其實就是距離itemView的四個方向的偏移量,是指偏移 ItemView 各個方向的數值,在上面的例子中,咱們設置了outRect.top=1,因此每一個ItemView之間有1px的空隙,因此呈現出1px灰色的分割線,分割線顏色決定於RecyclerView的背景色。.net

1、設置高度:

既然知道了這四個參數表明相對itemview的偏移,那麼分割線的高度就好辦了。

4

如圖,想要紅色那樣高度的分割線,只須要outRect.top等於該高度就能夠了。咱們將該高度定義爲mDividerHeight

2、設置顏色、左右偏移:

高度有了,若是咱們只想繪製紅色那部分矩形而不是ItemView上方的所有該怎麼辦?咱們知道每個View中的onDraw()方法是用來繪製組件的UI效果,因此想要顏色的話,須要咱們重寫ItemDecoration中的onDraw()方法。

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);
複製代碼

能夠看到onDraw()方法中有參數Canvas,經過它來繪製紅色矩形,因此咱們須要知道該矩形的四條邊的位置。

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

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

5

安卓中座標是這樣的,向下向右爲正,因此紅色矩形頂部位置就應該是itemView的top位置-矩形高度

偏移的話左側加上偏移量,右側減去偏移量便可。

c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
複製代碼

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

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

經過以上步驟,帶有顏色和偏移量,且具備必定高度的分割線就畫好了,其實還沒完,須要注意:getItemOffsets 是針對每個 ItemView,而 onDraw 方法倒是針對 RecyclerView 自己,因此在 onDraw 方法中須要遍歷屏幕上可見的 ItemView,分別獲取它們的位置信息,而後分別的繪製對應的分割線。

代碼以下:

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

        int childCount = parent.getChildCount();	//可見ItemView個數
//由於getItemOffsets是針對每個ItemView,而onDraw方法是針對RecyclerView自己,因此須要循環遍從來設置
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            //第一個ItemView不須要繪製
            if (index == 0) {
                continue;//跳過本次循環體中還沒有執行的語句,當即進行下一次的循環條件判斷
            }
          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);
        }
    }
複製代碼

在實際運用中,咱們的分割線顏色高度等樣式可能不同,這裏咱們經過建造者模式來設置這些屬性

//設置左右偏移(默認是設置的同樣的,若須要本身更改)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }
    //設置顏色
    public MyDecoration setColor(int color) {
        mPaint.setColor(color);
        return this;
    }
    //設置分割線高度
    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);        //默認顏色
    }

    //經過建造者模式來設置三個屬性
    //設置左右偏移(默認是設置的同樣的,若須要本身更改)
    public MyDecoration setMargin(float margin) {
        this.margin = margin;
        return this;
    }

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

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

    //在這裏就已經把寬度的偏移給作好了
    @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自己,因此須要循環遍從來設置
        for (int i = 0; i < childCount; i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            //第一個ItemView不須要繪製
            if (index == 0) {
                continue;//跳過本次循環體中還沒有執行的語句,當即進行下一次的循環條件判斷
            }
            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轉爲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地址

總結一下

一共兩步:

一、經過getItemOffsets()在itemView頂部撐出來一片區域

二、經過onDraw()方法來在該區域內繪製想要顏色及偏移量的分割線

其實ItemDecoration還有不少很牛逼的地方,例如實現時光軸效果,排行榜的角標,能夠看看我參考的那篇文章的實現,寫得很詳細的,是真大佬!往後Demo寫出來了再來更新

相關文章
相關標籤/搜索