上篇文章介紹了利用ItemDecoration的getItemOffsets方法來設(shè)置每個ItemView的間隔,這篇文章繼續(xù)介紹下ItemDecoration剩下的兩個方法(未過時的)——onDraw和onDrawOver。
在官方的開發(fā)文檔中有指出,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先會調(diào)用draw方法,在draw中又會調(diào)用onDraw方法。 而在RecyclerView的draw方法中會先通過super.draw() 調(diào)用父類也就是View的draw方法,進而繼續(xù)調(diào)用RecyclerView的OnDraw方法,ItemDecorations的onDraw方法就在此時會被調(diào)用,RecyclerView執(zhí)行完super.draw()之后,ItemDecorations的onDrawOver方法也被調(diào)用,這也就解釋了為什么說onDraw會繪制在itemview之前,表現(xiàn)形式是在最底層(抽象的說法,最底層應(yīng)該是background),onDrawOver是在itemview繪制之后,表現(xiàn)形式在最上層。如果你覺得有點繞的話,可以看看下面的部分源碼:
/**
* RecyclerView的draw方法
* @param c
*/
@Override
public void draw(Canvas c) {
// 調(diào)用父類也就是View的draw方法
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
// 執(zhí)行ItemDecorations的onDrawOver方法
mItemDecorations.get(i).onDrawOver(c, this, mState);
}
}
/**
* View的draw方法
* @param canvas
*/
@CallSuper
public void draw(Canvas canvas) {
....
// View會繼續(xù)調(diào)用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++) {
// 執(zhí)行ItemDecorations的onDraw方法
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
通過上面的分析,我們大概的了解了它的原理,接下來再來看看怎么用吧。
一般情況下,一個列表中的item除了上篇文章中有間隔那種表現(xiàn)形式,還有另一種帶分割線的表現(xiàn)是形式。如下圖所示:

之前在使用ListView的時候,可以通過在xml中定義中divider dividerHeight來實現(xiàn),不過RecyclerView并沒有這些屬性,在未使用ItemDecoration時,一般都是在Item的xml布局的底部設(shè)置一個View來充當Divider,然后在ViewHolder根據(jù)position來控制,這樣同樣顯得ViewHolder中有很一些看上去很不爽的代碼。
那么問題來了,用ItemDecoration怎么實現(xiàn)呢?其實很簡單,只需要在上篇文章的基礎(chǔ)上,把間隔處繪制相應(yīng)顏色就行了,廢話不多說,我們來實現(xiàn)吧。
-
通過構(gòu)造方法傳入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); } -
設(shè)置每個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是否設(shè)置了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 應(yīng)該是 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對調(diào)一下 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都能實現(xiàn)上圖的效果,因為這里并沒有層級之分,當有其他的需求時,我們只需要記住onDraw在繪制ItemView之前繪制,onDrawOver會在繪制ItemView之后繪制,然后根據(jù)實際情況處理就行了。下一篇,我們再用ItemDecoration實現(xiàn)更好玩的——stickHeader的效果,先來個圖,預(yù)告下。
