<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 更加丰富。