<font face = 黑體>毫無疑問,RecyclerView 是 Android 中最重要的系統組件之一,它的出現就是爲了高效代替 ListView 和 GridView。java
<font face = 黑體>今天,咱們來說講 RecyclerView 中的靜態內部類 ItemDecoration。顧名思義 ItemDecoration 就是 Item 的裝飾,咱們能夠在 Item 的上下左右添加自定義的裝飾,從而豐富 Item 的 UI 效果。android
<font face = 黑體>咱們都知道 RecyclerView 裏面就是一個又一個的 Item,可是其實這些 Item 外面包裹着一個矩形,只是咱們再使用 RecyclerView 的時候 Left、Right、Top 和 Bottom 默認都是 0,因此咱們看不到這些矩形,具體以下圖所示:
canvas
<font face = 黑體>咱們都知道,使用 RecyclerView 時 ,咱們不能像 ListView 那樣經過 setDivider() 的方式來設置分割線,可是系統已經爲咱們提供了一個 DividerItemDecoration 類來設置分割線,這個類就是繼承 RecyclerView.ItemDecoration,咱們來看下源碼:ide
public class DividerItemDecoration extends RecyclerView.ItemDecoration { public static final int HORIZONTAL = LinearLayout.HORIZONTAL; public static final int VERTICAL = LinearLayout.VERTICAL; private static final String TAG = "DividerItem"; private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; ... ... ... ...
<font face = 黑體>不設置分割線,效果以下所示:
學習
<font face = 黑體>DividerItemDecoration 的使用很是簡單,只需添加下面代碼便可:this
DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL); recyclerView.addItemDecoration(decoration);
<font face = 黑體>具體效果以下所示:
<font face = 黑體>通常狀況下以上 RecyclerView 的基本用法即可以實現絕大多數需求,可是某些場景下卻遠遠不夠,特別是須要實現比較複雜的 UI 效果的時候,因此這時候就須要利用 ItemDecoration,接下來咱們就學習一下 ItemDecoration 的具體使用。spa
<font face = 黑體>首先,咱們來看一下 ItemDecoration 的源碼,這裏的源碼已經把註釋和三個已經被棄用的方法去掉了,具體以下所示:3d
public abstract static class ItemDecoration { public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) { onDraw(c, parent); } public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) { onDrawOver(c, parent); } public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); } }
<font face = 黑體>從源碼中咱們能夠看出 ItemDecoration 類中就只有三個方法,分別是 onDraw()、onDrawOver() 和 getItemOffsets(),具體做用以下:code
<font face = 黑體>getItemOffsets() 的做用就是設置 Item 的內嵌偏移長度,從上面咱們已經知道,RecyclerView 的 Item 外面是包裹着一個矩形的,這個方法就是用來設置矩形與 Item 之間的間隔的。對象
<font face = 黑體>具體使用:
public class RectItemDecoration extends RecyclerView.ItemDecoration { // 參數說明: // 1. outRect:全爲 0 的 Rect(包括着Item) // 2. view:RecyclerView 中的 視圖 Item // 3. parent:RecyclerView 自己 // 4. state:狀態 @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { // 4個參數分別對應左(Left)、上(Top)、右(Right)、下(Bottom) // 上述語句表明:左、右、上和下偏移長度=100px outRect.set(100, 100, 100,100); } }
//用自定義分割線類設置分割線 recyclerView.addItemDecoration(new RectItemDecoration());
<font face = 黑體>效果以下所示:(上下左右都設置了 100px 的偏移量)
<font face = 黑體>onDraw() 的做用是經過 Canvas 對象繪製內容,須要注意的是 onDraw() 繪製會先於 Item 的繪製,因此若是在 onDraw() 中繪製的內容在 Item 邊界內,就會被 Item 遮擋住,因此 onDraw() 通常會和 getItemOffsets() 結合一塊兒使用,即在矩形與 Item 的間隔區域內繪製內容。
<font face = 黑體>實例1:在左側間隔區域繪製一個空心圓,並在下側間隔區域繪製一個 20px 的紅色分割線。
<font face = 黑體>咱們先來看效果:
<font face = 黑體>具體代碼以下所示:
public class CircleRectDecoration extends RecyclerView.ItemDecoration { private Paint colorPaint; private Paint dividerPaint; // 初始化 public RectItemDecoration() { colorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); colorPaint.setColor(Color.BLUE); colorPaint.setStyle(Paint.Style.STROKE); colorPaint.setStrokeWidth(5); dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); dividerPaint.setColor(Color.RED); dividerPaint.setStyle(Paint.Style.FILL); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.set(100, 0, 0, 20); } @Override public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(canvas, parent, state); RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); // 須要遍歷每一個 Item 進行繪製 for (int i = 0; i < parent.getChildCount(); i++) { View childView = parent.getChildAt(i); int leftItemWidth = layoutManager.getLeftDecorationWidth(childView); int bottomItemHeight = layoutManager.getBottomDecorationHeight(childView); int left = leftItemWidth / 2; canvas.drawCircle(left, childView.getTop() + childView.getHeight() / 2, 20, colorPaint); // getItemOffsets() 中的設置的是 bottom = 20px;因此在 drawRect 時,top 爲 childView.getBottom, bottom 爲top + bottomDecorationHeight canvas.drawRect(new Rect(leftItemWidth, childView.getBottom(), childView.getWidth() + leftItemWidth, childView.getBottom() + bottomItemHeight), dividerPaint); } }
<font face = 黑體>getItemOffsets() 是針對每一個 item 都會執行一次,也就是說每一個 item 的 outRect 能夠設置爲不一樣值,可是 onDraw() 是針對 ItemDecoration 的,不是針對 item 的,只執行一次。因此咱們在 onDraw() 中繪製的時候,須要遍歷每一個 item 進行繪製。
<font face = 黑體>onDrawOver() 的做用與 onDraw() 相似,都是經過 Canvas 對象繪製內容,但與onDraw() 的區別是:onDrawOver() 繪製是後於 onDraw() 的,因此繪製內容是不會被 Item 所遮擋的,反而 Item 會被 onDrawOver() 繪製的內容所遮擋。
<font face = 黑體>實例2:在實例1的基礎上,繪製一個角標到 Item 上。
<font face = 黑體>咱們先來看效果:
<font face = 黑體>具體代碼以下所示:
@Override public void onDrawOver(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(canvas, parent, state); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); int index = parent.getChildAdapterPosition(view); float top = view.getTop(); if (index < 3) { canvas.drawBitmap(mIcon, 120, top, flagPaint); } } }
<font face = 黑體>自定義 ItemDecoration 一般要根據須要,複寫它的 3 個方法。
<font face = 黑體>ItemDecoration 的做用遠不止以上這麼一點,我這裏只是簡單的介紹了下 ItemDecoration 中三個方法的使用,從而使得 RecyclerView 中的 Item 的 UI 更加豐富。