上篇文章介绍了利用ItemDecoration
的getItemOffsets
方法来设置每个ItemView
的间隔,这篇文章继续介绍下ItemDecoration
剩下的两个方法(未过时的)——onDraw
和onDrawOver
。
在官方的开发文档中有指出,onDraw
是在itemview
绘制之前,onDrawOver
是在itemview
绘制之后。
All ItemDecorations are drawn in the order they were added, before the item views (in onDraw() and after the items (in onDrawOver(Canvas, RecyclerView, RecyclerView.State).
相信稍微了解过Android中View的绘制流程的都知道,View先会调用draw
方法,在draw
中又会调用onDraw
方法。 而在RecyclerView
的draw
方法中会先通过super.draw()
调用父类也就是View
的draw
方法,进而继续调用RecyclerView
的OnDraw
方法,ItemDecorations
的onDraw
方法就在此时会被调用,RecyclerView
执行完super.draw()
之后,ItemDecorations
的onDrawOver
方法也被调用,这也就解释了为什么说onDraw
会绘制在itemview
之前,表现形式是在最底层(抽象的说法,最底层应该是background),onDrawOver
是在itemview
绘制之后,表现形式在最上层。如果你觉得有点绕的话,可以看看下面的部分源码:
/**
* RecyclerView的draw方法
* @param c
*/
@Override
public void draw(Canvas c) {
// 调用父类也就是View的draw方法
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
// 执行ItemDecorations的onDrawOver方法
mItemDecorations.get(i).onDrawOver(c, this, mState);
}
}
/**
* View的draw方法
* @param canvas
*/
@CallSuper
public void draw(Canvas canvas) {
....
// View会继续调用onDraw
if (!dirtyOpaque) onDraw(canvas);
....
}
/**
* RecyclerView的onDraw方法
* @param c
*/
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
// 执行ItemDecorations的onDraw方法
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
通过上面的分析,我们大概的了解了它的原理,接下来再来看看怎么用吧。
一般情况下,一个列表中的item除了上篇文章中有间隔那种表现形式,还有另一种带分割线的表现是形式。如下图所示:
之前在使用ListView
的时候,可以通过在xml中定义中divider
dividerHeight
来实现,不过RecyclerView
并没有这些属性,在未使用ItemDecoration
时,一般都是在Item
的xml
布局的底部设置一个View
来充当Divider
,然后在ViewHolder
根据position
来控制,这样同样显得ViewHolder
中有很一些看上去很不爽的代码。
那么问题来了,用ItemDecoration
怎么实现呢?其实很简单,只需要在上篇文章的基础上,把间隔处绘制相应颜色就行了,废话不多说,我们来实现吧。
-
通过构造方法传入divider的高度(
orientation
为HORIZONTAL
时表示宽度),还有颜色。public DividerDecoration(int height, Context ctx) { this(height, Color.GRAY, ctx); } public DividerDecoration(int height, @ColorInt int color, Context ctx) { this.mDividerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, ctx.getResources().getDisplayMetrics()); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(color); }
-
设置每个Item间的间隔,留出空间画
divider
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { if (((LinearLayoutManager) layoutManager).getOrientation() == LinearLayoutManager.HORIZONTAL) { if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) outRect.set(0, 0, (int) mDividerHeight, 0); } else { if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) outRect.set(0, 0, 0, (int) mDividerHeight); } } }
-
画
divider
@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { RecyclerView.LayoutManager manager = parent.getLayoutManager(); if (manager instanceof LinearLayoutManager) { if (((LinearLayoutManager) manager).getOrientation() == LinearLayoutManager.VERTICAL) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } } /** * 画divider (orientation为vertical) * * @param c * @param parent */ private void drawVertical(Canvas c, RecyclerView parent) { // recyclerView是否设置了paddingLeft和paddingRight final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); // divider的top 应该是 item的bottom 加上 marginBottom 再加上 Y方向上的位移 final int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child)); // divider的bottom就是top加上divider的高度了 final int bottom = (int) (top + mDividerHeight); c.drawRect(left, top, right, bottom, mPaint); } } /** * 画divider (当orientation为horizontal) * * @param c * @param parent */ private void drawHorizontal(Canvas c, RecyclerView parent) { // 和drawVertical差不多 left right 与 top和bottom对调一下 final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child)); final int right = (int) (left + mDividerHeight); c.drawRect(left, top, right, bottom, mPaint); } }
其实重写onDraw
或者onDrawOver
都能实现上图的效果,因为这里并没有层级之分,当有其他的需求时,我们只需要记住onDraw
在绘制ItemView
之前绘制,onDrawOver
会在绘制ItemView
之后绘制,然后根据实际情况处理就行了。下一篇,我们再用ItemDecoration
实现更好玩的——stickHeader
的效果,先来个图,预告下。