現(xiàn)在自己所寫的項目中用到列表的地方基本全部使用的是RecyclerView烤蜕,而使用列表就會有非常大的幾率用到分割線功能卖子。以前在用到RecyclerView的分割線功能的時候斩萌,都是利用RecyclerView自身設(shè)置的背景色和每個Item的背景色的差異玻褪,然后繼承RecyclerView.ItemDecoration许师,并且簡單的重寫
getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
方法就能達到效果了房蝉。簡單實現(xiàn)一個分割線效果代碼如下
public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
switch (mOrientation) {
case LinearLayoutManager.HORIZONTAL:
outRect.right += mSpace;
if (parent.getChildAdapterPosition (view) == 0)
outRect.left += mSpace;
break;
case LinearLayoutManager.VERTICAL:
outRect.bottom += mSpace;
if (parent.getChildAdapterPosition (view) == 0)
outRect.top += mSpace;
break;
}
}
這樣就能實現(xiàn)最簡單的一個分割線的效果了∥⑶可是今天在寫項目的時候遇到了分割線顏色可能會變動的情況搭幻。因此上面這種簡單的方式就不能滿足要求了。
首先逞盆,Google了一番發(fā)現(xiàn)檀蹋,原來ItemDecoration能做的事遠遠不止這么簡單,它還能定制出更多的酷炫的效果云芦,不僅能在“Item下面”繪制分割線效果,還能在“Item上面”蓋章。要定制更多的效果就要重寫ItemDecoration的另外兩個方法
onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)
和
onDrawOver (Canvas c, RecyclerView parent, RecyclerView.State state)
onDraw方法要比Item 的onDraw方法先執(zhí)行盒件,所以它畫出的東西總是在Item的下面寸潦,在 onDraw 為 divider 設(shè)置繪制范圍,并繪制到 canvas 上琉历,而這個繪制范圍可以超出在 getItemOffsets 中設(shè)置的范圍坠七,但由于 decoration 是繪制在 childView 的底下水醋,所以并不可見,但是會存在overDraw灼捂。而onDrawOver 方法是在Item的onDraw執(zhí)行之后才執(zhí)行离例,所以onDrawOver畫出的東西是在Item的上面。仔細學(xué)習(xí)了一下onDrawOver方法悉稠,簡直像發(fā)現(xiàn)了新大陸一樣宫蛆。此次項目中,有個列表左邊需要展示一張圖片的猛,中間是4行文字描述耀盗,然后在右邊還是一張圖片,不過這張圖片要有蓋在文字上的效果類似于下面這樣
我原本是用xml布局文件拼湊了這個效果叛拷。但是,仔細研究了onDrawOver方法后岂却,發(fā)現(xiàn)用onDrawOver也可以很好的實現(xiàn)此效果忿薇。xml文件代碼行數(shù)固然減少了很多而且加載效率肯定也有所提升,不過最重要的是感覺B格瞬間上升了很大一截
下面先放上自定義ItemDecoration的完整代碼
public class MyItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
private int mSize;
private int mOrientation;
private Paint mPaint;
private Bitmap bitmap;
public MyItemDecoration (Context context, int color, int drawableId, int size, int orientation) {
mDivider = new ColorDrawable (color);
mSize = size;
mOrientation = orientation;
bitmap = BitmapFactory.decodeResource (context.getResources (), drawableId);
mPaint = new Paint ();
mPaint.setColor (Color.RED);
mPaint.setStyle (Paint.Style.FILL);
}
@Override
public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
switch (mOrientation){
case LinearLayoutManager.HORIZONTAL:
outRect.right += mSize;
break;
case LinearLayoutManager.VERTICAL:
outRect.bottom += mSize;
break;
}
}
@Override
public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
int left, right, top, bottom;
if (mOrientation == LinearLayoutManager.VERTICAL) {
left = parent.getPaddingLeft ();
right = parent.getWidth () + parent.getPaddingRight ();
int count = parent.getChildCount ();
for (int i = 0; i < count; i++) {
View child = parent.getChildAt (i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams ();
top = child.getBottom () + params.bottomMargin;
bottom = top + mSize;
mDivider.setBounds (left, top, right, bottom);
mDivider.draw (c);
}
}
}
@Override
public void onDrawOver (Canvas c, RecyclerView parent, RecyclerView.State state) {
int left, top;
if (mOrientation == LinearLayoutManager.VERTICAL) {
int childCount = parent.getChildCount ();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt (i);
left = child.getWidth () - bitmap.getWidth () - 15;
top = child.getBottom () / 2 + (child.getBottom () / 2 * i) / (i + 1) - bitmap.getHeight () / 2;
c.drawBitmap (bitmap, left, top, mPaint);
}
}
}
}
下面是自己測試的實現(xiàn)效果
感覺很完美躏哩,右邊“異呈鸷疲”標(biāo)簽的位置也可以通過計算調(diào)整。
在自己摸索著寫這個MyItemDecoration 的過程中扫尺,遇到了個問題一并記錄一下筋栋。以前都是簡單的用,并沒有深入的了解過ItemDecoration正驻,也算給自己提個醒弊攘,以后不管學(xué)習(xí)還是工作,盡量多多的去延伸知識面
問題:只重寫onDraw方法也可以實現(xiàn)分割線的效果姑曙,那么getItemOffsets 的作用是又是什么呢襟交?
在遇到這個問題的時候,我覺得我占了一點好運氣渣磷,因為我給每個Item都設(shè)置了一個邊框婿着,如果沒有這個邊框,可能我會以為只重寫onDraw的效果就是正確的醋界,因為它“看起來”的效果確實是正確的竟宋。只重寫onDraw方法的時候出現(xiàn)的效果是這樣的
有分割線的效果,但是邊框卻“越過”了分割線和下一個Item相連了形纺,于是丘侠,我又重寫getItemOffsets方法,此時的效果就正常了逐样,如下
可是蜗字,為什么會出現(xiàn)“越過”分割線的效果呢打肝?經(jīng)過我自己多次對照代碼運行測試后發(fā)現(xiàn),畫第一個分割線的開始位置是這樣計算的
public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
......
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt (i);
top = child.getBottom () + params.bottomMargin;
bottom = top + mSize;
......
}
取得Item1.getBottom()作為第一條分割線的top挪捕,然后讓top加上設(shè)置的分割線的高度mSize作為第一條分割線的bottom粗梭,余下的分割線以此類推,此時不重寫getItemOffsets的情況下级零,畫個草圖更加一目了然
雖然通過onDraw方法畫出了分割線的效果断医,但是由于沒有重寫getItemOffsets()方法,所以RecyclerView的每個Item還是按照原始的布局位置設(shè)置奏纪,從3個紅色箭頭也可以看出
圖片頂部到Item1 top的距離 > 圖片到Item(x) top的距離鉴嗤,(x=2,3,4...)
這樣從Item2開始每個Item的頂部都少了一截mSize大小的高度,這就也解釋了為什么會出現(xiàn)邊框“越過”分割線的原因