之前用 ItemDecoration 一直都是用的源码里唯一附带的 DividerItemDecoration。 或者直接在每个Item里写分割线代码了。
一直没怎么管 ItemDecoration。
这段时间因为需要用到所以学习了下用法。
这个类需要继承 RecyclerView.ItemDecoration,有3种方法可以重写。
getItemOffsets 名字表示 item 的偏移,实际的工作就是按照需求对 item 的四个方向增加多少 padding。
onDraw 表示画 item 前进行的绘画。
onDrawOver 表示画 item 后进行的绘画。
所以这三者的顺序是 onDraw --> 画Item内容 --> onDrawOver
理论上,如果你将要画的分割线区域不会占据item的所要显示的核心内容(废话),直接用 drawOver 方法就行。
但是这会带来『过度绘制』, 不符合我们开发规范。所以一般要用 getItemOffsets 产生偏移,让item偏移出一些没有绘制的padding区域。
写了一个标准的网格布局分割线
亮点是绝对是偏移和绘制分割线都对的上,『调试GPU过度绘制』检查妥妥的完美不重叠。 不像网上找来的一大片过度绘制。
public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {
//使用系统自带的分割线
// private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private int spanCount = -1; //一行有多少列
private int childCount = -1; //有多少个Item
public GridDividerItemDecoration(Context context) {
// final TypedArray a = context.obtainStyledAttributes(ATTRS);
// mDivider = a.getDrawable(0);
// a.recycle();
//使用自己定义的分割线
mDivider = context.getResources().getDrawable(R.drawable.my_divider);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int visibleItemCount = parent.getChildCount();
for (int i=0; i<visibleItemCount; i++) {
View view = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
//画竖线
if (!isLastCol(i, getSpanCount(parent))) {
int left = view.getRight() + params.rightMargin;
int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
int top = view.getTop() - params.topMargin;
int bottom = view.getBottom() + params.bottomMargin;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
//画横线
if (!isLastRow(i, getSpanCount(parent), getChildCount(parent))) {
int left = view.getLeft() - params.leftMargin;
int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
int top = view.getBottom() + params.bottomMargin;
int bottom = view.getBottom() + mDivider.getIntrinsicHeight() + params.bottomMargin;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
/**
* 获取列数
* @param parent
* @return
*/
private int getSpanCount(RecyclerView parent) {
if (spanCount == -1){
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
}
return spanCount;
}
/**
* 获取item总数
* @param parent
* @return
*/
private int getChildCount(RecyclerView parent) {
if (childCount == -1) {
childCount = parent.getAdapter().getItemCount();
}
return childCount;
}
/**
* 判断是否最后一列
* @param pos
* @param spanCount
* @return
*/
private boolean isLastCol(int pos, int spanCount) {
if ((pos + 1) % spanCount == 0) {
return true;
}
return false;
}
/**
* 判断是否最后一行
* @param pos
* @param spanCount
* @param childCount
* @return
*/
private boolean isLastRow(int pos, int spanCount, int childCount) {
int lastCompletedCount;
if (childCount % spanCount == 0){
lastCompletedCount = (childCount / spanCount - 1) * spanCount;
} else {
lastCompletedCount = childCount - childCount % spanCount;
}
if (pos >= lastCompletedCount)
return true;
return false;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int spanCount = getSpanCount(parent);
int childCount = getChildCount(parent);
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
int right = mDivider.getIntrinsicWidth();
int bottom = mDivider.getIntrinsicHeight();
//最后一行bottom不用偏移
if (isLastRow(itemPosition, spanCount, childCount)){
bottom = 0;
}
//最后一列right不用偏移
if (isLastCol(itemPosition, spanCount)){
right = 0;
}
outRect.set(0, 0, right, bottom);
}
}
对于分割线颜色和大小:
可以像 DividerItemDecoration 那样,使用了默认的l分割线。 实际就是颜色是#DCDCDC,大小宽高都是2px的分割线。
也可以自定义,需要写一个 drawable,比如
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#32f2" />
<size
android:width="1dp"
android:height="1dp" />
</shape>
填对应的了颜色和宽高值。宽代表竖分割线的宽度,高代表横分割线的宽度。
对于 getItemOffsets 的偏移:
outRect.set(0, 0, right, bottom);是用一个Rect类来存放四个方向的偏移量。
然后会在 LayoutManager 和 RecyclerView 中的测量 measure 和放置 layout 每个 Item 逻辑里用到(不同LayoutManager用到的地方不一样)。用以扩大整个item的区域,好给之后的draw方法有可绘画的地方。
如果设置的是Vertical方向滚动。那就是在左右方向的outRect会是Item的向内收缩。上下方向是Item的向外扩张。