上篇文章介紹了利用ItemDecoration
的getItemOffsets
方法來(lái)設(shè)置每個(gè)ItemView
的間隔背零,這篇文章繼續(xù)介紹下ItemDecoration
剩下的兩個(gè)方法(未過(guò)時(shí)的)——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).
相信稍微了解過(guò)Android中View的繪制流程的都知道吃溅,View先會(huì)調(diào)用draw
方法溶诞,在draw
中又會(huì)調(diào)用onDraw
方法。 而在RecyclerView
的draw
方法中會(huì)先通過(guò)super.draw()
調(diào)用父類也就是View
的draw
方法决侈,進(jìn)而繼續(xù)調(diào)用RecyclerView
的OnDraw
方法螺垢,ItemDecorations
的onDraw
方法就在此時(shí)會(huì)被調(diào)用,RecyclerView
執(zhí)行完super.draw()
之后赖歌,ItemDecorations
的onDrawOver
方法也被調(diào)用枉圃,這也就解釋了為什么說(shuō)onDraw
會(huì)繪制在itemview
之前,表現(xiàn)形式是在最底層(抽象的說(shuō)法庐冯,最底層應(yīng)該是background)孽亲,onDrawOver
是在itemview
繪制之后,表現(xiàn)形式在最上層展父。如果你覺(jué)得有點(diǎ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會(huì)繼續(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);
}
}
通過(guò)上面的分析,我們大概的了解了它的原理犯祠,接下來(lái)再來(lái)看看怎么用吧旭等。
一般情況下,一個(gè)列表中的item除了上篇文章中有間隔那種表現(xiàn)形式衡载,還有另一種帶分割線的表現(xiàn)是形式搔耕。如下圖所示:
之前在使用ListView
的時(shí)候,可以通過(guò)在xml中定義中divider
dividerHeight
來(lái)實(shí)現(xiàn),不過(guò)RecyclerView
并沒(méi)有這些屬性弃榨,在未使用ItemDecoration
時(shí)菩收,一般都是在Item
的xml
布局的底部設(shè)置一個(gè)View
來(lái)充當(dāng)Divider
,然后在ViewHolder
根據(jù)position
來(lái)控制鲸睛,這樣同樣顯得ViewHolder
中有很一些看上去很不爽的代碼娜饵。
那么問(wèn)題來(lái)了,用ItemDecoration
怎么實(shí)現(xiàn)呢官辈?其實(shí)很簡(jiǎn)單箱舞,只需要在上篇文章的基礎(chǔ)上,把間隔處繪制相應(yīng)顏色就行了拳亿,廢話不多說(shuō)晴股,我們來(lái)實(shí)現(xiàn)吧。
-
通過(guò)構(gòu)造方法傳入divider的高度(
orientation
為HORIZONTAL
時(shí)表示寬度)肺魁,還有顏色电湘。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è)置每個(gè)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 (當(dāng)orientation為horizontal) * * @param c * @param parent */ private void drawHorizontal(Canvas c, RecyclerView parent) { // 和drawVertical差不多 left right 與 top和bottom對(duì)調(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); } }
其實(shí)重寫onDraw
或者onDrawOver
都能實(shí)現(xiàn)上圖的效果鹅经,因?yàn)檫@里并沒(méi)有層級(jí)之分寂呛,當(dāng)有其他的需求時(shí),我們只需要記住onDraw
在繪制ItemView
之前繪制瘾晃,onDrawOver
會(huì)在繪制ItemView
之后繪制昧谊,然后根據(jù)實(shí)際情況處理就行了。下一篇,我們?cè)儆?code>ItemDecoration實(shí)現(xiàn)更好玩的——stickHeader
的效果酗捌,先來(lái)個(gè)圖呢诬,預(yù)告下。