RecyclerView分割线详解
具体的原理可以参考底部留下的第一个地址
用法可以参考第二个地址
1. 添加RecyclerView分割线,继承自RecyclerView.ItemDecoration
- 可实现3个方法
- onDraw()
- getItemOffsets()
- onDrawOver()
方法间相互关系
1. 方法执行的顺序为:
getItemOffsets() 执行4次 –> onDraw() 执行1次 –> child view onDraw() 执行1次 –> onDrawOver() 执行1次
通过Log日志可以看到
getItemOffsets() position = 0
getItemOffsets() position = 1
getItemOffsets() position = 2
getItemOffsets() position = 3
onDraw()
onDrawOver()
2. 方法的作用:
2.1 onDraw()
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {...}
顾名思义,跟View的onDraw()方法一样,分割线的绘制,这个方法中有熟悉的Canvas对象,拿它来直接画就行。但是画的位置需要通过获取RecyclerView的item来确定。
比如在第一个item的上面我要个20dp的分割线:
c.drawRect(childView.getLeft(), childView.getTop() - 20dp, childView.getRight(), childView.getTop(), mPaint);
childView就是这个第一项item,根据这个item的上下左右的位置来确定分割线的位置,从而绘制分割线。而item的位置首先是按照原始的排列,比如本例中的从上到下,依次排列,item中间没有空隙。但是在getItemOffsets()方法中通过Rect的设置来增加item的左上右下的Padding值从而与其他item产生距离。
在上面方法的执行流程中可以看到,getItemOffsets()方法会首先执行多次,而在方法中设置的outRect左上右下的值每个item被onMeasure()测量方法间接调用 加在item的Padding中,从而item之间会产生间隔。
然后调用分割线的onDraw()方法绘制
然后每个item的onDraw()方法调用
然后onDrawOver()方法调用
由此可知,在分割线的onDraw()方法中绘制的内容可能会被item的onDraw()中绘制的内容挡住,而onDrawOver()方法中绘制的内容会在最上层。
2.2 getItemOffsets()
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {...}
如上所讲,在getItemOffsets()方法中通过Rect的设置来增加item的左上右下的Padding值。
outRect.set(0, 20dp, 0, 10dp);
就是在item顶部与底部分别在padding中加相应的值。
2.3 onDrawOver()
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {...}
与onDraw()方法相同,只是执行的顺序不同。
talk is cheap show me the code
功能描述:
1. 分为两部分,第一部分是上面的一行行选项,第二部分是下面的提示,其中选项可以有一个或多个,而提示只有一个
2. 分割线主要体现在选项中,第一个选项距顶部30dp,最后一个选项距底部20dp,选项之间相距10dp。
mRecyclerView.addItemDecoration(mDividerItemDecoration);
mDividerItemDecoration.setItemCount(3);
class DividerItemDecoration extends RecyclerView.ItemDecoration {
private final float TOP_DIVIDER_HEIGHT = getResources().getDimension(R.dimen.promote_limit_list_top_divider_height);
private final float MIDDLE_DIVIDER_HEIGHT = getResources().getDimension(R.dimen.promote_limit_list_middle_divider_height);
private final float BOTTOM_DIVIDER_HEIGHT = getResources().getDimension(R.dimen.promote_limit_list_bottom_divider_height);
private int mItemCount;
Paint mPaint = new Paint();
public void setItemCount(int itemCount) {
this.mItemCount = itemCount + 1;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mItemCount < 2) {
return;
}
mPaint.setColor(getResources().getColor(R.color.promote_limit_divider_color));
//获取RecyclerView中所有子项
int count = parent.getChildCount();
for (int i = 0; i < count; i++) {
//获取RecyclerView中指定位置的子项
View childView = parent.getChildAt(i);
if (mItemCount == 2) {
//所有子项只有两项,既只有一个选项的时候
if (i == 0) {
//第一个选项要求距顶部30dp,距底部20dp
c.drawRect(childView.getLeft(), childView.getTop() - (int) TOP_DIVIDER_HEIGHT, childView.getRight(), childView.getTop(), mPaint);
c.drawRect(childView.getLeft(), childView.getBottom(), childView.getRight(), childView.getBottom() + BOTTOM_DIVIDER_HEIGHT, mPaint);
}
} else {
//所有子项大于两项,既多于一个选项的时候
if (i == 0) {
//第一个选项要求距顶部30dp,距底部10dp
c.drawRect(childView.getLeft(), childView.getTop() - (int) TOP_DIVIDER_HEIGHT, childView.getRight(), childView.getTop(), mPaint);
c.drawRect(childView.getLeft(), childView.getBottom(), childView.getRight(), childView.getBottom() + MIDDLE_DIVIDER_HEIGHT, mPaint);
} else if (i == count - 2) {
//最后一个选项要求距底部20dp,最后一个选项是所有子项的倒数第二个
c.drawRect(childView.getLeft(), childView.getBottom(), childView.getRight(), childView.getBottom() + BOTTOM_DIVIDER_HEIGHT, mPaint);
} else if (i == count - 1) {
//最后一项是提示,不做处理
} else {
//其他选项,距底部10dp
c.drawRect(childView.getLeft(), childView.getBottom(), childView.getRight(), childView.getBottom() + MIDDLE_DIVIDER_HEIGHT, mPaint);
}
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
int position = params.getViewAdapterPosition();
if (mItemCount < 2) {
return;
}
//与上面的逻辑一样
if (mItemCount == 2) {
if (position == 0) {
outRect.set(0, (int) TOP_DIVIDER_HEIGHT, 0, (int) BOTTOM_DIVIDER_HEIGHT);
}
} else {
if (position == 0) {
outRect.set(0, (int) TOP_DIVIDER_HEIGHT, 0, (int) MIDDLE_DIVIDER_HEIGHT);
} else if (position == mItemCount - 2) {
outRect.set(0, 0, 0, (int) BOTTOM_DIVIDER_HEIGHT);
} else if (position == mItemCount - 1) {
} else {
outRect.set(0, 0, 0, (int) MIDDLE_DIVIDER_HEIGHT);
}
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
Log.d("myinfo", "onDrawOver");
}
}
参考:
https://blog.piasy.com/2016/03/26/Insight-Android-RecyclerView-ItemDecoration/
87
http://ju.outofmemory.cn/entry/249642
https://gold.xitu.io/entry/58482343128fe1006c6291c1