ItemDecoration是对item进行装饰的一个抽象类,只要实现它对应的方法就可以实现对应的功能,先来看下这个类:
public abstract static class ItemDecoration {
// 在onDraw()方法中被调用到
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
/**
* @deprecated
* Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDraw(Canvas c, RecyclerView parent) {
}
// RecyclerView重写了view的draw()方法,在draw()方法中被调用到,也就是当item被绘制完后会调用到这个方法(即最后被绘制)
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
/**
* @deprecated
* Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
*/
@Deprecated
public void onDrawOver(Canvas c, RecyclerView parent) {
}
/**
* @deprecated
* Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
*/
@Deprecated
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
outRect.set(0, 0, 0, 0);
}
// 会在测量中被调用到,outRect中的left,top,right,bottom四个距离最终会被加到margin中,可以理解为就是设置item的margin值
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
getItemOffsets()的调用时机和设置说明:
// 测量view所需要的空间大小
public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
// getItemOffsets()中设置的值会在下面这个方法中被调用到
final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
// 计算长宽方面的总值
widthUsed += insets.left + insets.right;
heightUsed += insets.top + insets.bottom;
// 计算childView长所需要的总空间大小
final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
getPaddingLeft() + getPaddingRight()
+ lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
canScrollHorizontally());
// 计算childView高所需要的总空间大小
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() + getPaddingBottom()
+ lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
canScrollVertically());
if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
child.measure(widthSpec, heightSpec);
}
}
// 这里就是去收集getItemOffsets()返回的值
Rect getItemDecorInsetsForChild(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.mInsetsDirty) {
return lp.mDecorInsets;
}
if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
// changed/invalid items should not be updated until they are rebound.
return lp.mDecorInsets;
}
// 注意这里,insets变量使用的是LayoutManager中的mDecorInsets变量,所以下次使用的时候直接使用LayoutManager中的mDecorInsets就可以了
final Rect insets = lp.mDecorInsets;
insets.set(0, 0, 0, 0);
final int decorCount = mItemDecorations.size();
// 这里就是遍历所有添加进来的ItemDecoration
for (int i = 0; i < decorCount; i++) {
mTempRect.set(0, 0, 0, 0);
mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
insets.left += mTempRect.left;
insets.top += mTempRect.top;
insets.right += mTempRect.right;
insets.bottom += mTempRect.bottom;
}
lp.mInsetsDirty = false;
return insets;
}
从这里还看不出getItemOffsets()设置的就是margin,这里只是计算出了item所需要的空间大小,再布局的时候还有一个layoutDecoratedWithMargins()方法:
public void layoutDecoratedWithMargins(View child, int left, int top, int right,
int bottom) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets = lp.mDecorInsets;
child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
right - insets.right - lp.rightMargin,
bottom - insets.bottom - lp.bottomMargin);
}
从这里就可以看出,getItemOffsets()设置的值最终都是会加到margin中去,这也就说明了getItemOffsets()中设置的值其实可以理解为设置margin值。
onDraw()和onDrawOver()的区别
在我看来,这两个方法的最主要区别就是调用的先后顺序,onDraw()先于onDrawOver()调用,下面来看下RecyclerView中代码的实现:
@Override
public void draw(Canvas c) {
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDrawOver(c, this, mState);
}
... ...
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
在view的方法中调用了onDraw()方法,上面的draw()方法先调用了super.draw(),这也就是说会先调用到onDraw(),接着调用的就是onDrawOver()方法,这样看,是不是整个逻辑就比较清晰了。如果想来个例子,可以看看
android v7包里面提供的DividerItemDecoration这个类,它实现的是在item之间添加分割线,到此ItemDecoration就结束了。