想到分割线,原先一直是在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);
}
}
复制代码
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
以上方法,经过使每一个ItemView向上撑出1px距离,而RecyclerView背景为灰色,这样就显示出1px的灰色线,实现分割线功能,看到这你可能会想,这也太粗糙了,若是我想要改变分割线的宽度、颜色该怎么办,总不能每写一个RecyclerView都再写一套分割器,更改RecyclerView背景颜色吧,并且通常分割线并不占满所有宽度,有左右偏移,那该怎么实现呢?this
别急,咱们先了解下getItemOffsets()方法中的outRect这个参数。spa
其中的蓝色部分为咱们的RecyclerView的子项ItemView,外部黄色部分为outRect,只是黄色,并不包含ItemView压的那部分,left,right,top,bottom四个参数其实就是距离itemView的四个方向的偏移量,是指偏移 ItemView 各个方向的数值,在上面的例子中,咱们设置了outRect.top=1
,因此每一个ItemView之间有1px的空隙,因此呈现出1px灰色的分割线,分割线颜色决定于RecyclerView的背景色。.net
1、设置高度:
既然知道了这四个参数表明相对itemview的偏移,那么分割线的高度就好办了。
如图,想要红色那样高度的分割线,只须要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的顶部加上分割线的高度,咦?我怎么写的减号?看下图你应该就会明白
安卓中坐标是这样的,向下向右为正,因此红色矩形顶部位置就应该是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);
}
复制代码
看下效果:
到这里咱们想要的功能就所有完成了。(终于码完了开心!)
若是咱们的RecyclerView是横向的滑动,原理相似,剩下的就交给大家了(懒得写了嘿嘿)
一共两步:
一、经过getItemOffsets()在itemView顶部撑出来一片区域
二、经过onDraw()方法来在该区域内绘制想要颜色及偏移量的分割线
其实ItemDecoration还有不少很牛逼的地方,例如实现时光轴效果,排行榜的角标,能够看看我参考的那篇文章的实现,写得很详细的,是真大佬!往后Demo写出来了再来更新