Android 自定义 ItemDecoration 分割线
01.需要实现的分割线功能
- 需要实现的分割线功能
- 可以设置分割线的颜色,宽度,以及到左右两边的宽度间距。
02.ItemDecoration方法说明
几个重要的方法说明
- 需要自定义类实现RecyclerView.ItemDecoration类,并选择重写合适方法。注意下面这三个方法有着强烈的因果关系!
1
2
3
4
5
6//获取当前view的位置信息,该方法主要是设置条目周边的偏移量
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
//在item背后draw
public void onDraw(Canvas c, RecyclerView parent, State state)
//在item上边draw
public void onDrawOver(Canvas c, RecyclerView parent, State state)
03.方法调用顺序
- 注意的是三个方法的调用顺序
- 首先调用的是getItemOffsets会被多次调用,在layoutManager每次测量可摆放的view的时候回调用一次,在当前状态下需要摆放多少个view这个方法就会回调多少次。
- 其次会调用onDraw方法,ItemDecoration的onDraw方法是在RecyclerView的onDraw方法中调用的,注意这时候传入的canvas是RecyclerView的canvas,要时刻注意这点,它是和RecyclerView的边界是一致的。这个时候绘制的内容相当于背景,会被item覆盖。
- 最后调用的是onDrawOver方法,ItemDecoration的onDrawOver方法是在RecyclerView的draw方法中调用的,同样传入的是RecyclerView的canvas,这时候onlayout已经调用,所以此时绘制的内容会覆盖item。
04.实现item分割线思路
- 为每个item实现索引的思路
- 要实现上面的可以设置分割线颜色和宽度,肯定是要绘制的,也就是需要使用到onDraw方法。那么在getItemOffsets方法中需要让view摆放位置距离bottom的距离是分割线的宽度。
- 然后通过parent.getChildCount()方法拿到当前显示的view的数量[注意,该方法并不会获取不显示的view的数量],循环遍历后,直接用paint画笔进行绘制[注意至于分割线的颜色就是需要设置画笔的颜色]。
05.完整性代码展示
代码如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175/**
* list条目的分割线,可以设置线条颜色和宽度
*/
public class RecycleViewItemLine extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Drawable mDivider;
/**
* 分割线高度,默认为1px
*/
private int mDividerHeight = 1;
/**
* 列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
*/
private int mOrientation;
private static int[] ATTRS = new int[]{android.R.attr.listDivider};
/**
* 默认分割线:高度为2px,颜色为灰色
* @param context 上下文
* @param orientation 列表方向
*/
public RecycleViewItemLine(Context context, int orientation) {
if (orientation != LinearLayoutManager.VERTICAL &&
orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("请输入正确的参数!");
}
mOrientation = orientation;
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
/**
* 自定义分割线
* @param context 上下文
* @param orientation 列表方向
* @param drawableId 分割线图片,或者shape图形
*/
public RecycleViewItemLine(Context context, int orientation, int drawableId) {
this(context, orientation);
mDivider = ContextCompat.getDrawable(context, drawableId);
if (mDivider != null) {
mDividerHeight = mDivider.getIntrinsicHeight();
}
}
/**
* 自定义分割线
* @param context 上下文
* @param orientation 列表方向
* @param dividerHeight 分割线高度
* @param dividerColor 分割线颜色
*/
public RecycleViewItemLine(Context context, int orientation,
int dividerHeight, int dividerColor) {
this(context, orientation);
mDividerHeight = dividerHeight;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//设置画笔paint的颜色
mPaint.setColor(dividerColor);
//设置样式
mPaint.setStyle(Paint.Style.FILL);
}
/**
* 调用的是getItemOffsets会被多次调用,在layoutManager每次测量可摆放的view的时候回调用一次,
* 在当前状态下需要摆放多少个view这个方法就会回调多少次。
* @param outRect 核心参数,这个rect相当于item摆放的时候设置的margin,
* rect的left相当于item的marginLeft,
* rect的right相当于item的marginRight
* @param view 当前绘制的view,可以用来获取它在adapter中的位置
* @param parent recyclerView
* @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);
//给bottom留出一个高度为mDividerHeight的空白
//这样做的目的是什么呢?就是为下面onDraw方法绘制高度为mDividerHeight的分割线做准备用的
outRect.set(0, 0, 0, mDividerHeight);
RefreshLogUtils.d("RecycleViewItemLine-------"+"getItemOffsets");
}
/**
* 绘制分割线
* ItemDecoration的onDraw方法是在RecyclerView的onDraw方法中调用的
* 注意这时候传入的canvas是RecyclerView的canvas,要时刻注意这点,它是和RecyclerView的边界是一致的。
* 这个时候绘制的内容相当于背景,会被item覆盖。
* @param c canvas用来绘制的画板
* @param parent recyclerView
* @param state 状态,用的很少
*/
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
RefreshLogUtils.d("RecycleViewItemLine-------"+"onDraw");
}
/**
* 绘制分割线
* ItemDecoration的onDrawOver方法是在RecyclerView的draw方法中调用的
* 同样传入的是RecyclerView的canvas,这时候onLayout已经调用,所以此时绘制的内容会覆盖item。
* @param c canvas用来绘制的画板
* @param parent recyclerView
* @param state 状态,用的很少
*/
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
super.onDrawOver(c, parent, state);
RefreshLogUtils.d("RecycleViewItemLine-------"+"onDrawOver");
}
/**
* 绘制横向 item 分割线
*/
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
RefreshLogUtils.d("小杨逗比左右的间距分别是" + left + "----"+right);
//获取的当前显示的view的数量,并不会获取不显示的view的数量。
//假如recyclerView里共有30条数据,而当前屏幕内显示的只有5条,这paren.getChildCount的值是5,不是30。
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
//获取索引i处的控件view
final View child = parent.getChildAt(i);
//拿到layoutParams属性
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)
child.getLayoutParams();
final int top = child.getBottom() + layoutParams.bottomMargin;
final int bottom = top + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
//使用画笔paint进行绘制
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
/**
* 绘制纵向 item 分割线
*/
private void drawVertical(Canvas canvas, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)
child.getLayoutParams();
final int left = child.getRight() + layoutParams.rightMargin;
final int right = left + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
}