通过ItemDecoration,可以给RecyclerView或者RecyclerView中的每个Item添加额外的装饰效果,最常用的就是用来为Item之间添加分割线,今天,我们就来一起学习有关的知识:
APIDividerItemDecoration解析ItemDecorationAPI介绍当我们实现自己的ItemDecoration时,需要继承于ItemDecoration,并根据需要实现以下三个方法:
public void onDraw(Canvas c, RecyclerView parent, State state)canvas:RecyclerView的canvasparent:RecyclerView实例State:RecyclerView当前的状态,值包括START/LAYOUT/ANIMATION。所有在这个方法中的绘制操作,将会在itemViews被绘制之前执行,因此,它会显示在itemView之下。
public void onDrawOver(Canvas c, RecyclerView parent, State state)和2.1方法类似,区别在于它绘制在itemViews之上。
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)通过outRect,可以设置item之间的间隔,间隔区域的大小就是outRect所指定的范围,view就是对应位置的itemView,其它的参数解释和上面相同。
DividerItemDecoration解析上面我们解释了需要重写的方法以及其中参数的含义,下面,我们通过官方自带的DividerItemDecoration,来进一步加深对这些方法的认识。DividerItemDecoration是为LinearLayoutManager提供的分割线,在创建它的时候,需要指定ORIENTATION,这个方向应当和LinearLayoutManager的方向相同。
private void init() {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_content);
mTitles = new ArrayList<>();
for (int i = 0; i < 20; i++) {
mTitles.add(String.valueOf(i));
}
BaseAdapter baseAdapter = new BaseAdapter(mTitles);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(baseAdapter);
}
最终展示的效果为:

DividerItemDecoration重写了基类当中的onDraw方法,也就是说这个分割线是在itemView之前绘制的:
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
我们先看纵向排列的RecyclerView分割线:
@SuppressLint("NewApi")
private void drawVertical(Canvas canvas, RecyclerView parent) {
//首先保存画布
canvas.save();
final int left;
final int right;
//确定左右边界的范围,如果RecyclerView不允许子View绘制在Padding内,那么这个范围为去掉Padding后的范围
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
//获得itemView的范围,这个范围包括了margin和offset,它们被保存在mBounds当中
parent.getDecoratedBoundsWithMargins(child, mBounds);
//需要考虑translationY和translationY
final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));
//由于是垂直排列的,因此上边界等于下边界减去分割线的高度.
final int top = bottom - mDivider.getIntrinsicHeight();
//设置divider和范围
mDivider.setBounds(left, top, right, bottom);
//绘制.
mDivider.draw(canvas);
}
//回复画布.
canvas.restore();
}
整个过程分为三步:
View在RecyclerView中的绘制范围View的范围mDivider的绘制范围下图就是最终计算的结果:

横向排列的RecyclerView列表和上面的原理是相同的,区别就在于计算mDivider.setBounds的计算:
//....
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));
final int left = right - mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
//..
从上面的分析可以知道,如果将divider直接绘制在itemView的范围内,那么由于我们是先绘制divider,再绘制itemView的内容的,那么它就会被覆盖,因此,通过重写getItemOffsets,通过其中的outRect来指定留出的空隙:
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (mOrientation == VERTICAL) {
//如果是纵向排列,那么要在itemView的下方留出一个下边界
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
//如果是横向排列,那么要在itemView的右方留出一个右边界
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
ItemDecoration下面,我们参考上面的写法,写一个简单的GridLayoutManager的分割线:
public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
private final Rect mBounds = new Rect();
public GridDividerItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
public void setDrawable(@NonNull Drawable drawable) {
mDivider = drawable;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawDivider(c, parent);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
}
private void drawDivider(Canvas canvas, RecyclerView parent) {
canvas.save();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = parent.getChildAt(i);
parent.getDecoratedBoundsWithMargins(view, mBounds);
mDivider.setBounds(mBounds.right - mDivider.getIntrinsicWidth(), mBounds.top, mBounds.right, mBounds.bottom);
mDivider.draw(canvas);
mDivider.setBounds(mBounds.left, mBounds.bottom - mDivider.getIntrinsicHeight(), mBounds.right , mBounds.bottom);
mDivider.draw(canvas);
}
canvas.restore();
}
}
最终的效果为:

这里我们为了演示方便,没有考虑最后一列或者最后一行没有分割线的情况,这篇文章写的比较好:Android RecyclerView 使用完全解析 体验艺术般的控件。
ItemDecoration的使用并不难,大多数情况下就只需要重写onDraw和onDrawOver中的一个;如果需要在Item之间添加间隔,那么要重写getItemOffsets并理解outRect的含义,假如不需要添加间隔,那么不需要重写该方法。
RecyclerView 知识梳理(4) - ItemDecoration
原文:http://www.cnblogs.com/youseiraws/p/7071156.html