上篇文章介紹了利用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
時表示寬度),還有顏色。```java
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
的效果,先來個圖,預告下。
作者:static_sadhu
鏈接:http://www.jianshu.com/p/6a093bcc6b83