24

自定义ItemDecoration分割线的高度、颜色、偏移,看完这个你就懂了

 4 years ago
source link: https://juejin.im/post/5cecef7d5188250b3a1b9173
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2019年05月28日 阅读 3889

自定义ItemDecoration分割线的高度、颜色、偏移,看完这个你就懂了

想到分割线,原先一直是在item的布局中直接加入,在adapter中进行判断,若是最后一个子项则将分割线隐藏,感觉太小儿科了,今天来好好研究这个ItemDecoration的使用。

文章参考自RecyclerView 之 ItemDecoration 讲解及高级特性实践,写的很详细,仔细看后就会用了,我只是在此基础上增添了可以更改颜色、宽度、左右偏移的功能。废话不多说,咱们开始做吧。

简单的添加分割线:

一、建立工程,创建Adapter,加载布局文件

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

二、建立分割器

提前说明,以下内容均是在每个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;
        }
    }
}
复制代码

加入后的效果:

2

自定义颜色偏移宽度的分割器

以上方法,通过使每个ItemView向上撑出1px距离,而RecyclerView背景为灰色,这样就显示出1px的灰色线,实现分割线功能,看到这你可能会想,这也太粗糙了,如果我想要改变分割线的宽度、颜色该怎么办,总不能每写一个RecyclerView都再写一套分割器,更改RecyclerView背景颜色吧,而且一般分割线并不占满全部宽度,有左右偏移,那该怎么实现呢?

别急,我们先了解下getItemOffsets()方法中的outRect这个参数。

3

其中的蓝色部分为我们的RecyclerView的子项ItemView,外部黄色部分为outRect,只是黄色,并不包含ItemView压的那部分,left,right,top,bottom四个参数其实就是距离itemView的四个方向的偏移量,是指偏移 ItemView 各个方向的数值,在上面的例子中,我们设置了outRect.top=1,所以每个ItemView之间有1px的空隙,所以呈现出1px灰色的分割线,分割线颜色决定于RecyclerView的背景色。

一、设置高度:

既然知道了这四个参数代表相对itemview的偏移,那么分割线的高度就好办了。

4

如图,想要红色那样高度的分割线,只需要outRect.top等于该高度就可以了。我们将该高度定义为mDividerHeight

二、设置颜色、左右偏移:

高度有了,如果我们只想绘制红色那部分矩形而不是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地址

一共两步:

1、通过getItemOffsets()在itemView顶部撑出来一片区域

2、通过onDraw()方法来在该区域内绘制想要颜色及偏移量的分割线

其实ItemDecoration还有很多很牛逼的地方,例如实现时光轴效果,排行榜的角标,可以看看我参考的那篇文章的实现,写得很详细的,是真大佬!日后Demo写出来了再来更新


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK