1.前言
RecyclerVIew是我们日常开发中使用频率最高的控件了吧,之前由于比较马虎没好好整理过如何添加分割线,以前是通过文件或者在布局里投机取巧进行添加,现如今有空有必要好好的整理一番了。
宏观把控Recycyerview.ItemDecoration实现的方法
public class MyDecoration extends RecyclerView.ItemDecoration{
/**
* step1 获得条目的偏移量 ---- 就相当于空出一块矩形空间 会在矩形空间里进行绘制
* 目标针对每一个item个体
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
/**
* step2 在item间绘制 常用于绘制分割线
* 针对整个Recyclerview 绘制需要循环遍历item子布局 然后方能针对具体的item进行增加绘制
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
}
/**
* 在item上绘制
* @param c
* @param parent
* @param state
*/
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
}
2.1无任何分割线的RecyclerView
private void initAdapter() {
mMyRecvAdapter = new MyRecvAdapter(mContext,mList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setAdapter(mMyRecvAdapter);
}
相信大家对如何说些一个RecyclerView列表再熟悉不过了吧,不过推荐大家可以使用BRVAH点击打开链接出来蛮久的了,可以大大提升开发效率。
2.2 一行代码添加系统分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
运行效果:
分析一下: RecyclerView 为我们提供的DividerItemDecoration的用于添加分割线的类中 代码其实不多,主要是继承了ItemDecoration并实现了 getItemOffsets( )和onDraw()方法,这里的getItemOffsets( )实际上是在条目之间分配一块矩形区域用来放置我们的分割线,注意一下方法参数Rect outRect,而通过onDraw( )方法把条目画在刚刚申请的间隔矩形中。没错就是这么简单,有兴趣的可以对照着源码细看一下。
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
......
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
// 设置矩形区域
// set(left,top,right,bottom) = outRect.left = left ...
}
@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);
}
}
.......
}
2.3 不用系统的分割线,自己设定分割线的宽度和颜色
看下面只需要知己设置drawable就可以了,你想要什么形状就可以添加什么样的形状。
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
dividerItemDecoration.setDrawable(ContextCompat.getDrawable(this,R.drawable.shape_devider_line_red));
mRecyclerView.addItemDecoration(dividerItemDecoration);
3. 扩展
前面已经分析了DeivderItemDecoration的实现了,我们也可以自己手动撸一个来实现不是?我们可以发现前面虽然是可以设置drawable进去,那么每次还需要写一个drawable文件或者弄张.9图进去,但通常的业务环境是只需要带颜色的线条就可以了吧,所以基于此我们可以自己手写一个。
直接上代码了额:
/**
* @author zxl on 2018/7/14.
* discription: 自定义分割线支持水平和垂直布局 设置默认,图片和颜色为分割线
*/
public class MyRecyclerViewDivider extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Drawable mDivider;
private int mDividerHeight;
private int mOrientation;
/**
* 系统默认的分割线
* */
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
/**
* 设置默认的分割线
* @param orientation 指代分割线的方向
* */
public MyRecyclerViewDivider(Context context, int orientation) {
this.mDividerHeight = 2;
if(orientation != 1 && orientation != 0) {
throw new IllegalArgumentException("请输入正确的参数!");
} else {
this.mOrientation = orientation;
TypedArray a = context.obtainStyledAttributes(ATTRS);
this.mDivider = a.getDrawable(0);
a.recycle();
}
}
/**
* 设置图片资源为分割线
* */
public MyRecyclerViewDivider(Context context, int orientation, int drawableId) {
this(context, orientation);
this.mDivider = ContextCompat.getDrawable(context, drawableId);
this.mDividerHeight = this.mDivider.getIntrinsicHeight();
}
/**
* 设置线条颜色和高度给分割线
* */
public MyRecyclerViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
this(context, orientation);
this.mDividerHeight = dividerHeight;
this.mPaint = new Paint(1);
this.mPaint.setColor(dividerColor);
this.mPaint.setStyle(Paint.Style.FILL);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, this.mDividerHeight);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if(this.mOrientation == 1) {
this.drawVertical(c, parent);
} else {
this.drawHorizontal(c, parent);
}
}
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
canvas.save();
final int left;
final int right;
// 判断RecyclerView是否要裁剪padding值
if (parent.getClipToPadding()){
// 需要裁剪那么就进行裁剪
left = parent.getPaddingLeft();
right = parent.getWidth()-parent.getPaddingRight();
// 裁剪rv可视区域, 限制视图在该区域内是可见的,很重要的,这里
canvas.clipRect(left,parent.getPaddingTop(),right,parent.getHeight()-parent.getPaddingBottom());
}else {
// 不裁剪则宽贼为rv的宽
left = 0;
right = parent.getWidth();
}
int childSize = parent.getChildCount();
for(int i = 0; i < childSize; ++i) {
View child = parent.getChildAt(i);
// 获得item的信息包
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)child.getLayoutParams();
// 分割线顶部 = item的底部 + item到底部的距离 + 动画偏移
int top = child.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
// 分割线底部 = 分割线底部 + 高度
int bottom = top + this.mDividerHeight;
if(this.mDivider != null) {
this.mDivider.setBounds(left, top, right, bottom);
this.mDivider.draw(canvas);
}
if(this.mPaint != null) {
canvas.drawRect((float)left, (float)top, (float)right, (float)bottom, this.mPaint);
}
}
canvas.restore();
}
private void drawVertical(Canvas canvas, RecyclerView parent) {
canvas.save();
final int top;
final int bottom;
if (parent.getClipToPadding()){
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(),top,parent.getWidth()-parent.getPaddingRight(),bottom);
}else {
top = 0;
bottom = parent.getHeight();
}
int childSize = parent.getChildCount();
for(int i = 0; i < childSize; ++i) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)child.getLayoutParams();
int left = child.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(child));
int right = left + this.mDividerHeight;
if(this.mDivider != null) {
this.mDivider.setBounds(left, top, right, bottom);
this.mDivider.draw(canvas);
}
if(this.mPaint != null) {
canvas.drawRect((float)left, (float)top, (float)right, (float)bottom, this.mPaint);
}
}
canvas.restore();
}
}
// RecyclerView parent
parent.getDecoratedBoundsWithMargins(child,mBounds);
int bottom = mBounds.bottom+Math.round(child.getTranslationY());
int top = bottom - dividerSize;
用法如下也是很简单,优点不用多说既囊括了系统提供的也支持drawable设置而且支持简易的线条颜色等,用途很大对的吧。
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(this, LinearLayoutManager.HORIZONTAL,2,ContextCompat.getColor(this,R.color.colorAccent)));
效果:
弄个表格出来
以下为 addItemDecoration的源码,可以发现的是可以设置多条分割线,哈哈,那么我们不就能画表格了嘛,6666
public void addItemDecoration(ItemDecoration decor, int index) {
if (mLayout != null) {
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
+ " layout");
}
if (mItemDecorations.isEmpty()) {
setWillNotDraw(false);
}
if (index < 0) {
mItemDecorations.add(decor);
} else {
mItemDecorations.add(index, decor);
}
markItemDecorInsetsDirty();
requestLayout();
}
activity中的代码如下:
mMyRecvAdapter = new MyRecvAdapter(mContext,mList);
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(this, LinearLayoutManager.HORIZONTAL,2,ContextCompat.getColor(this,R.color.colorAccent)));
mRecyclerView.addItemDecoration(new MyRecyclerViewDivider(this,LinearLayoutManager.VERTICAL,2,ContextCompat.getColor(this,R.color.colorAccent)));
mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
mRecyclerView.setAdapter(mMyRecvAdapter);
运行效果如上图所示。