Android(安卓)RecyclerView —— 自定义分割线
Android RecyclerView —— 自定义分割线
Android RecyclerView —— 基本使用
Android RecyclerView —— 适配器封装探索
前面说了 RecyclerView
的基本使用以及对适配器的封装,但是在使用 ListView
时,有 dividerHeight
和 divider
属性用来设置分割线的高度和颜色(Drawable
),但是在 RecyclerView
中并没有这个属性,而且也没有替代的属性,也就是说,在 RecyclerView
并我们不能直接设置分割线,而需要使用 RecyclerView
提供的方法 addItemDecoration(@NonNull RecyclerView.ItemDecoration decor)
来增加分割线。 RecyclerView.ItemDecoration
需要由我们自己实现。主要需要实现的有三个方法(真正实现的其实只有2个):
// 在 item 绘制之前调用(就是绘制在 item 的底层) [和 onDrawOver() 方法二选一即可]public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { this.onDraw(c, parent);}// 在 item 绘制之后调用(就是绘制在 item 的上层) [和 onDraw() 方法二选一即可]public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { this.onDrawOver(c, parent);}// 设置偏移量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);}
getItemOffsets()
实际上是在条目之间分配一块矩形区域用来放置我们的分割线,注意一下方法参数Rect outRect,而通过 onDraw()
方法或者 onDrawOver()
方法把颜色(Drawable)画在刚刚申请的间隔矩形中;这样就相当于增加了分割线。
提示:在 support 包的版本是 25或以上时,系统提供了一个默认绘制分割线的实现 DividerItemDecoration
,但是该实现只针对 LinearLayoutManager
。而对于 GridLayoutManager
并没有默认的实现,所以我们需要自定义实现。
实现简单的分割线类
以下代码实现了一个最简单的分割线类,支持 LinearLayoutManager
类型。
public class LinearDividerDecoration extends RecyclerView.ItemDecoration { private int mDividerHeight = 40; private int mDividerColor = 0xFFFF0000; private Paint mPaint; private int mOrientation; public LinearDividerDecoration(@RecyclerView.Orientation int orientation) { this.mOrientation = orientation; mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(mDividerColor); } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { if (mOrientation == LinearLayoutManager.VERTICAL) { outRect.set(0, 0, 0, mDividerHeight); } else { outRect.set(0, 0, mDividerHeight, 0); } } @Override public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { canvas.save(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; ++i) { View childAt = parent.getChildAt(i); int left = 0; int right = parent.getWidth(); int top = childAt.getBottom() ; int bottom = childAt.getBottom()+ mDividerHeight; canvas.drawRect(left, top, right, bottom, mPaint); } canvas.restore(); }}
使用:
// 增加分割线recyclerView.addItemDecoration(new LinearDividerDecoration(RecyclerView.VERTICAL));
效果如下:
同理,我们也可以使用类似的方式来实现支持 GridLayoutManager
的分割线类。
public class GridDividerDecoration extends RecyclerView.ItemDecoration { private int mDividerHeight = 4; private int mDividerColor = 0xFFFF0000; private Paint mPaint; private int mOrientation; public GridDividerDecoration(@RecyclerView.Orientation int orientation) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(mDividerColor); mOrientation = orientation; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager(); int itemCount = parent.getAdapter().getItemCount(); int viewLayoutPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); int left = 0; int top = 0; int right = mDividerHeight; int bottom = mDividerHeight; if (isLastRow(layoutManager, itemCount, viewLayoutPosition)) { // 如果是最后一行,则不需要绘制底部 bottom = 0; } if (isLastCol(layoutManager, itemCount, viewLayoutPosition)) { // 如果是最后一列,则不需要绘制右边 right = 0; } outRect.set(left, top, right, bottom); } /** * 判断是否最后一列 */ private boolean isLastCol(GridLayoutManager layoutManager, int childCount, int itemPosition) { GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup(); int spanCount = layoutManager.getSpanCount(); int spanIndex = spanSizeLookup.getSpanIndex(itemPosition, spanCount); int spanSize = spanSizeLookup.getSpanSize(itemPosition); if (mOrientation == GridLayoutManager.VERTICAL) { return spanIndex + spanSize == spanCount; } else { return (childCount - itemPosition) / (spanCount * 1.0f) <= 1; } } /** * 判断是否最后一行 */ private boolean isLastRow(GridLayoutManager layoutManager, int childCount, int itemPosition) { GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup(); int spanCount = layoutManager.getSpanCount(); int spanIndex = spanSizeLookup.getSpanIndex(itemPosition, spanCount); int spanSize = spanSizeLookup.getSpanSize(itemPosition); if (mOrientation == GridLayoutManager.VERTICAL) { return (childCount - itemPosition) / (spanCount * 1.0f) <= 1; } else { return spanIndex + spanSize == spanCount; } } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { c.save(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View childAt = parent.getChildAt(i); drawHorizontal(c, childAt); drawVertical(c, childAt); } c.restore(); } public void drawHorizontal(Canvas c, View childAt) { int left = childAt.getLeft(); int right = childAt.getRight(); int top = childAt.getBottom() ; int bottom = childAt.getBottom()+ mDividerHeight; c.drawRect(left, top, right, bottom, mPaint); } public void drawVertical(Canvas c, View childAt) { int left = childAt.getRight(); int right = left + mDividerHeight; int top = childAt.getTop(); int bottom = childAt.getBottom() + mDividerHeight; c.drawRect(left, top, right, bottom, mPaint); }}
使用:
// 增加分割线recyclerView.addItemDecoration(new GridDividerDecoration(GridLayoutManager.VERTICAL));
效果如下:
看上去好像没什么问题,但是我们把 mDividerHeight
调大到 50,效果图如下:
上图我们能明显的发现item的宽度不一样了,这是什么原因造成的了,这是因为分割线占用的控件是属于 item 的空间,那么当 GridLayoutManager
设置了 spanCount
时,RecyclerView
就会把整个控件的宽度平均分配成 spanCount 份,每一个item的大小都是一样的,但是因为我们在绘制列分割线时,最后一样没有绘制分割线,所以导致会后一列的item宽度明显大于前面的宽度,从而造成了item宽度不一样的问题。
解决 GridLayoutManager
添加了分割线之后 item 宽度不同的问题
看如下图:
说明:我们把整个item的宽度定义为width,然后左右的分割线宽度为 lw(需要的话),中间的分割线宽度为 dw,那么一共的分割线宽度就是:total = (spanCount - 1) * dw + 2 * lw;这样一来就能计算出每个item需要留出的空间:item = total / spanCount;因为每个item留出的相等的空间,那么item剩余的空间也是相同的了,也就是item的宽度也就是一样的了。
具体的代码实现,请看这里。对 RecyclerView
的分割线进行了封装,实现了自定义设置分割线宽、高,是都需要绘制四周的分割线,以及四周分割线的宽高,还有分割线的颜色、交叉点的颜色等。
效果图如下:
github代码地址
去github下载相关代码及实例
更多相关文章
- 安卓开发之 在应用中使用数据库
- Android中调用Paint的measureText()方法取得字符串显示的宽度值
- android创建optionsmenu的方法
- Android(安卓)注入Event
- Android之Activity的启动方式
- Android导入项目后,左上角报错的解决方法
- 小白学习android: google code 上源代码的下载方法
- View点击涟漪效果
- Android(安卓)两种串口实现方法总结