RecyclerView通过addItemDecoration()方法添加item之间的分割线。Android并没有提供实现好的Divider,因此任何分割线样式都需要自己实现。
自定义间隔样式需要继承RecyclerView.ItemDecoration类,该类是个抽象类,官方目前并没有提供默认的实现类,主要有三个方法。
onDraw(Canvas c, RecyclerView parent, State state),在Item绘制之前被调用,该方法主要用于绘制间隔样式。
onDrawOver(Canvas c, RecyclerView parent, State state),在Item绘制之前被调用,该方法主要用于绘制间隔样式。
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state),设置item的偏移量,偏移的部分用于填充间隔样式,即设置分割线的宽、高;在RecyclerView的onMesure()中会调用该方法。
onDraw()和onDrawOver()这两个方法都是用于绘制间隔样式,我们只需要复写其中一个方法即可。
Google在sample中给了一个参考的实现类:DividerItemDecoration,这里我们通过分析这个例子来看如何自定义Item Decoration。
自定义的间隔样式的实现步骤① 通过读取系统主题中的 Android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。
该分割线是系统默认的,你可以在theme.xml中找到该属性(android:listDivider)的使用情况。
如果要设置,则需要在value/styles.xml中设置:
② 获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect去设置了绘制的范围。
③ onDraw中实现了真正的绘制。
① 获取listDivider首先看构造函数,构造函数中获得系统属性android:listDivider,该属性是一个Drawable对象。
private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable mDivider; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } ② getItemOffsets接着来看getItemOffsets()的实现:
public void getItemOffsets(Rect outRect, int position, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } }这里只看mOrientation == VERTICAL_LIST的情况,outRect是当前item四周的间距,类似margin属性,现在设置了该item下间距为mDivider.getIntrinsicHeight()。
那么getItemOffsets()是怎么被调用的呢?
RecyclerView继承了ViewGroup,并重写了measureChild(),该方法在onMeasure()中被调用,用来计算每个child的大小,计算每个child大小的时候就需要加上getItemOffsets()设置的外间距:
public void measureChild(View child, int widthUsed, int heightUsed){ final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);//调用getItemOffsets()获得Rect对象 widthUsed += insets.left + insets.right; heightUsed += insets.top + insets.bottom; //... } ③ onDraw这里我们只考虑mOrientation == VERTICAL_LIST的情况,DividerItemDecoration的onDraw()实际上调用了drawVertical():
public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); // 画每个item的分割线 for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child)); final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom);/*规定好左上角和右下角*/ mDivider.draw(c); } }那么onDraw()是怎么被调用的呢?还有ItemDecoration还有一个方法onDrawOver(),该方法也可以被重写,那么onDraw()和onDrawOver()之间有什么关系呢?