我们知道想要给RecycleView添加一条分割线需要使用 mRecycleView.addItemDecoration()添加,Android为我们提供了类似于ListView默认分割线的DividerItemDecoration,我们将其添加到RecycleView即可得到分割线,下面就参考DividerItemDecoration来介绍下ItemDecoration。
public abstract static class ItemDecoration {
public ItemDecoration() {
}
/**
* 绘制RecycleView的背景
*/
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
this.onDraw(c, parent);
}
/**
* 绘制RecycleView的前景
*/
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
this.onDrawOver(c, parent);
}
/**
* 用于设置每一个item的偏移,每一个方向的偏移设置在outRect中
*/
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
this.getItemOffsets(outRect, ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
}
}
我们只需要继承ItemDecoration即可实现自己的分割线。getItemOffsets用与设置每一个item的偏移。如下所示
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//item的左边会有30px的距离
outRect.left = 30;
//item的左上右下的距离分别为 10 15 20 30px
//outRect.set(10,15,20,30);
}
outRect的上下左右会被当做item四周的间距。
onDraw方法用于绘制RecycleView的背景,我们需要在这个方法里面绘制我们需要绘制的东西。要想绘制分隔线,那么我们需要定位到分隔线所在的位置,错误示范。
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
//这会导致整个RecycleView的背景变为红色
c.drawColor(Color.RED);
}
要想准确的绘制,我们需要找到item的边界以及其他一些信息获取Item的position
/获取当前item的position
parent.getChildAdapterPosition(view);
parent.findViewHolderForAdapterPosition(view);
//通过LayoutParams获取当前item的position
//其实我们通过LayoutParams还可以获取很多其他的信息
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.getViewLayoutPosition();
//使用LayoutManager
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int itemCount = layoutManager.getItemCount();
获取Item中设置的布局占用的区域
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
//获取到子View
View childAt = parent.getChildAt(i);
//获取item的区域
parent.getDecoratedBoundsWithMargins(childAt, outBounds);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
//绘制红色
c.drawRect(outBounds, paint);
paint.setColor(Color.BLUE);
//直接获取item里面布局的区域,并绘制蓝色
c.drawRect(childAt.getLeft(),childAt.getTop(),childAt.getRight(),childAt.getBottom(),paint);
}
}
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//item的左上右下的距离分别为 10 15 20 30px
outRect.set(10,15,20,30);
}
通过上图可以看到我们在getItemOffsets中设置的边距生效了,并且每一个item的区域以及我们自己的布局所占用的空间也标示出来了。我们想绘制分隔线或者其他的一些什么就可以了自由发挥了,可以参考DividerItemDecoration的源码。不过要注意的是我们在onDraw中可以绘制整个RecycleView的背景,这点我们可以用来做时光轴onDrawOver是用来绘制RecycleView的前景的,就是说会绘制在Item的上面,我们可以用来绘制徽章等。
时光轴效果
private RecyclerView.ItemDecoration getItemDecoration() {
return new RecyclerView.ItemDecoration() {
private Paint paint = new Paint();
private int LineWidth = 80;
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
int viewCount = parent.getChildCount();
drawLine(c, LineWidth / 2, 0, LineWidth / 2, parent.getMeasuredHeight());
for (int i = 0; i < viewCount; i++) {
View child = parent.getChildAt(i);
drawDot(c, LineWidth / 2, child.getBottom() - child.getHeight() / 2, 10);
}
}
private void drawLine(Canvas c, int x1, int y1, int x2, int y2) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.CYAN);
c.drawLine(x1, y1, x2, y2, paint);
}
private void drawDot(Canvas c, int x, int y, int width) {
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setColor(Color.RED);
c.drawCircle(x, y, width, paint);
}
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//左边80px的区域用来绘制时光轴
outRect.left = LineWidth;
}
};
}
给RecycleView加上间距
垂直方向加上边距
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int mSpace = 0;
public SpacesItemDecoration(int space) {
mSpace = space;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getAdapter() == null) {
return;
}
int childLayoutPosition = parent.getChildLayoutPosition(view);
int itemCount = parent.getAdapter().getItemCount();
if (itemCount < 2) {
return;
}
if (childLayoutPosition == 0) {
outRect.bottom = mSpace / 2;
} else if (childLayoutPosition == itemCount - 1) {
outRect.top = mSpace / 2;
} else {
outRect.bottom = mSpace / 2;
outRect.top = mSpace / 2;
}
}
}
Grid布局
public class GridSpacesItemDecoration extends RecyclerView.ItemDecoration {
private int mSpaceOrientation;
private int mSpaceVertical;
public GridSpacesItemDecoration(int spaceOrientation, int spaceVertical) {
mSpaceOrientation = spaceOrientation;
mSpaceVertical = spaceVertical;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getAdapter() == null) {
return;
}
if (!(parent.getLayoutManager() instanceof GridLayoutManager)) {
throw new IllegalArgumentException(only support GridLayoutManager);
}
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
//一行多少个
int spanCount = layoutManager.getSpanCount();
//一共有多少个
int itemCount = parent.getAdapter().getItemCount();
//子布局所在的位置
int position = parent.getChildAdapterPosition(view);
//一共多少行 1开始
int rowCount = itemCount / spanCount;
//如果有不满一行的算一行
if (itemCount % spanCount > 0) {
rowCount = rowCount + 1;
}
//在第几行 0开始
int itemRowIndex = position / spanCount;
if (position - (itemRowIndex + 1) * spanCount > 0) {
itemRowIndex = itemRowIndex + 1;
}
//在第几列 0开始
int itemColumnIndex = position % spanCount;
//大于一列才有左右间隔
if (spanCount > 1) {
if (itemColumnIndex == 0) {
outRect.right = mSpaceOrientation / 2;
} else if (itemColumnIndex == spanCount - 1) {
outRect.left = mSpaceOrientation / 2;
} else {
outRect.right = mSpaceOrientation / 2;
outRect.left = mSpaceOrientation / 2;
}
}
//大于一行才有上下间隔
if (rowCount > 1) {
if (itemRowIndex == 0) {
outRect.bottom = mSpaceVertical / 2;
} else if (itemRowIndex == rowCount - 1) {
outRect.top = mSpaceVertical / 2;
} else {
outRect.top = mSpaceVertical / 2;
outRect.bottom = mSpaceVertical / 2;
}
}
}
}