很久之前接触过RecyclerView,前段时间闲来无事用了下,结果发觉又被分割线伤了下。于是找了分代码,理解和完善,形成了适合我的一个个万能分割线工具类。
主要方法:
/**
* 看图说话:get Item Offsets,获得item的偏移量。此方法用来控制item的偏移
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
/**
* 绘制分割线
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
}
多的原理不说了,去其他网站搜下吧。
不难,多花不说,分享代码。
全部代码,宁包入住。
package com.duke.test;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* author: duke
* version: 2.0
* dateTime: 2020-03-11 19:30
* description:
*/
public class TopicRecycleViewDivider extends RecyclerView.ItemDecoration {
// 绘制分割线的画笔
private Paint paint;
// 如果是画笔绘制,记录分割线宽度或高度 px
private int paintWidthPX = 5;
// 如果需要绘制给定的 drawable
private Drawable drawableDivider;
// 用画笔绘制颜色,还是绘制特定的drawable
private DrawType drawType;
// 注意:列表的方向,非分割线的方向
// LinearLayoutManager.HORIZONTAL 或 LinearLayoutManager.VERTICAL
private int orientation = LinearLayoutManager.VERTICAL;
// 是否需要忽略第一个 item 后面的分割线,有些需求需要
private boolean isSkipFirstItemBelowLine = false;
// 是否需要忽略最后一个 item 后面的分割线,大多数需求都需要
private boolean isSkipLaseItemBelowLine = true;
/**
* 忽略 第一个 item 下方或右侧的分割线。个别需要需要
*
* @param skipFirstItemBelowLine 是否忽略
* @return this
*/
public TopicRecycleViewDivider skipFirstItemBelowLine(boolean skipFirstItemBelowLine) {
isSkipFirstItemBelowLine = skipFirstItemBelowLine;
return this;
}
/**
* 忽略 最后一个 item 下方或右侧的分割线。一般都需要
*
* @param skipLaseItemBelowLine 是否忽略
* @return this
*/
public TopicRecycleViewDivider skipLaseItemBelowLine(boolean skipLaseItemBelowLine) {
isSkipLaseItemBelowLine = skipLaseItemBelowLine;
return this;
}
/**
* 设置列表方向,非分割线的
*
* @param listOrientation 列表方向,<br/>
* LinearLayoutManager.HORIZONTAL or LinearLayoutManager.VERTICAL <br/>
* @return this
*/
public TopicRecycleViewDivider setListOrientation(int listOrientation) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("Parameter of orientation is error. Please see LinearLayoutManager ...");
}
orientation = listOrientation;
return this;
}
/**
* 构造函数
*
* @param context context
* @param drawableId 分割线图片
*/
public TopicRecycleViewDivider(Context context, int drawableId) {
drawType = DrawType.USE_DRAWABLE;
drawableDivider = ContextCompat.getDrawable(context, drawableId);
}
/**
* 自定义分割线
*
* @param dividerHeightPX 分割线高度 px
* @param dividerColorInt 分割线颜色
*/
public TopicRecycleViewDivider(int dividerHeightPX, int dividerColorInt) {
drawType = DrawType.USE_PAINT;
paintWidthPX = dividerHeightPX;
// 绘制纯颜色 (之一:可以绘制纯颜色)
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(dividerColorInt);
paint.setStyle(Paint.Style.FILL);
}
/**
* 看图说话:get Item Offsets,获得item的偏移量。此方法用来控制item的偏移
*
* @param outRect outRect 表示在 item 的 上、下、左、右 四周撑开的距离,默认值为 0
* @param view 当前的 holder view
* @param parent recyclerView
* @param state state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int index = parent.getChildAdapterPosition(view);
// 忽略第一个 item 的分割线
if (isSkipFirstItemBelowLine && index == 0) {
return;
}
int totalSize = state.getItemCount();
// 忽略最后一个 item 的分割线
if (isSkipLaseItemBelowLine && index == totalSize - 1) {
return;
}
/**
* 列表的方向为横向,画分割线就是纵向的,需要确定的是 child 的右边偏移值
* 留出空间画分割线
*/
if (orientation == LinearLayoutManager.HORIZONTAL) {
int x = 0;
switch (drawType) {
case USE_PAINT:
x = paintWidthPX;
break;
case USE_DRAWABLE:
x = drawableDivider.getIntrinsicWidth();
break;
}
outRect.set(0, 0, x, 0);
}
/**
* 列表的方向为纵向,画分割线就是横向的,需要确定的是 child 的下边偏移值
* 留出空间画分割线
*/
else if (this.orientation == LinearLayoutManager.VERTICAL) {
int x = 0;
switch (drawType) {
case USE_PAINT:
x = paintWidthPX;
break;
case USE_DRAWABLE:
x = drawableDivider.getIntrinsicHeight();
break;
}
outRect.set(0, 0, 0, x);
}
}
/**
* 绘制分割线
*
* @param c canvas
* @param parent paint
* @param state state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (orientation == LinearLayoutManager.VERTICAL) {
// 列表是纵向的,需要绘制横向的分割线
drawHorizontalLine(c, parent);
} else {
// 列表是横向的,需要绘制纵向的分割线
drawVerticalLine(c, parent);
}
}
/**
* 绘制横向 item 分割线。左、上、右都是可计算的,下需要获取给定的高度值
*
* @param canvas canvas
* @param parent paint
*/
private void drawHorizontalLine(Canvas canvas, RecyclerView parent) {
// 左边:到父容器的 left 内间距位置值
final int left = parent.getPaddingLeft();
// 右边:到父容器的 right 内间距位置值
final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
final int childSize = parent.getChildCount();
// 循环绘制每条分割线
for (int i = 0; i < childSize; i++) {
if (isSkipFirstItemBelowLine && i == 0) {
continue;
}
if (isSkipLaseItemBelowLine && i == childSize - 1) {
continue;
}
final View child = parent.getChildAt(i);
if (!(child.getLayoutParams() instanceof RecyclerView.LayoutParams)) {
continue;
}
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
// 上边:具体的某条分割线的上边以 child 的 (bottom + bottomMargin) 位置值
final int top = child.getBottom() + layoutParams.bottomMargin;
// 下边:根据类型判断
int bottom;
switch (drawType) {
case USE_PAINT:
// 构造方法声明使用画笔绘制
// 下边:top 加上指定的高度
bottom = top + paintWidthPX;
canvas.drawRect(left, top, right, bottom, paint);
break;
case USE_DRAWABLE:
// 构造方法声明使用 drawable
// 下边:top 加上指定的高度
bottom = top + drawableDivider.getIntrinsicHeight();
drawableDivider.setBounds(left, top, right, bottom);
drawableDivider.draw(canvas);
break;
}
}
}
/**
* 绘制纵向 item 分割线。上、下、左都是可计算的,右侧需要获取给定的宽度值
*
* @param canvas canvas
* @param parent parent
*/
private void drawVerticalLine(Canvas canvas, RecyclerView parent) {
// 上边:到父容器的 top 内间距位置值
final int top = parent.getPaddingTop();
// 下边:到父容器的 bottom 内间距位置值
final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
final int childSize = parent.getChildCount();
// 循环绘制每条分割线
for (int i = 0; i < childSize; i++) {
if (isSkipFirstItemBelowLine && i == 0) {
continue;
}
if (isSkipLaseItemBelowLine && i == childSize - 1) {
continue;
}
final View child = parent.getChildAt(i);
if (!(child.getLayoutParams() instanceof RecyclerView.LayoutParams)) {
continue;
}
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
// 左边:具体的某条分割线的左边以 child 的 (right + rightMargin) 位置值
final int left = child.getRight() + layoutParams.rightMargin;
// 右边:根据类型判断
int right;
switch (drawType) {
case USE_PAINT:
// 构造方法声明使用画笔绘制
// 右边:left 加上指定的宽度
right = left + paintWidthPX;
canvas.drawRect(left, top, right, bottom, paint);
break;
case USE_DRAWABLE:
// 构造方法声明使用 drawable
// 右边:left 加上指定的宽度
right = left + drawableDivider.getIntrinsicWidth();
drawableDivider.setBounds(left, top, right, bottom);
drawableDivider.draw(canvas);
break;
}
}
}
public enum DrawType {
USE_PAINT(1), // 用画笔绘制纯颜色
USE_DRAWABLE(2); // 绘制特定的 drawable
private final int type;
DrawType(int type) {
this.type = type;
}
public int getType() {
return type;
}
}
}