之前用 ItemDecoration 一直都是用的源码里唯一附带的 DividerItemDecoration。 或者直接在每个Item里写分割线代码了。
一直没怎么管 ItemDecoration。

这段时间因为需要用到所以学习了下用法。
这个类需要继承 RecyclerView.ItemDecoration,有3种方法可以重写。
getItemOffsets 名字表示 item 的偏移,实际的工作就是按照需求对 item 的四个方向增加多少 padding。
onDraw 表示画 item 前进行的绘画。
onDrawOver 表示画 item 后进行的绘画。

所以这三者的顺序是  onDraw --> 画Item内容 --> onDrawOver
理论上,如果你将要画的分割线区域不会占据item的所要显示的核心内容(废话),直接用 drawOver 方法就行。
但是这会带来『过度绘制』, 不符合我们开发规范。所以一般要用 getItemOffsets 产生偏移,让item偏移出一些没有绘制的padding区域。



写了一个标准的网格布局分割线
亮点是绝对是偏移和绘制分割线都对的上,『调试GPU过度绘制』检查妥妥的完美不重叠。 不像网上找来的一大片过度绘制。

public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {

    //使用系统自带的分割线
//    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    private Drawable mDivider;
    private int spanCount = -1; //一行有多少列
    private int childCount = -1; //有多少个Item

    public GridDividerItemDecoration(Context context) {
//        final TypedArray a = context.obtainStyledAttributes(ATTRS);
//        mDivider = a.getDrawable(0);
//        a.recycle();

        //使用自己定义的分割线
        mDivider = context.getResources().getDrawable(R.drawable.my_divider);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int visibleItemCount = parent.getChildCount();
        for (int i=0; i<visibleItemCount; i++) {
            View view = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
            //画竖线
            if (!isLastCol(i, getSpanCount(parent))) {
                int left = view.getRight() + params.rightMargin;
                int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
                int top = view.getTop() - params.topMargin;
                int bottom = view.getBottom() + params.bottomMargin;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
            //画横线
            if (!isLastRow(i, getSpanCount(parent), getChildCount(parent))) {
                int left = view.getLeft() - params.leftMargin;
                int right = view.getRight() + mDivider.getIntrinsicWidth() + params.rightMargin;
                int top = view.getBottom() + params.bottomMargin;
                int bottom = view.getBottom() + mDivider.getIntrinsicHeight() + params.bottomMargin;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    }

    /**
     * 获取列数
     * @param parent
     * @return
     */
    private int getSpanCount(RecyclerView parent) {
        if (spanCount == -1){
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    /**
     * 获取item总数
     * @param parent
     * @return
     */
    private int getChildCount(RecyclerView parent) {
        if (childCount == -1) {
            childCount = parent.getAdapter().getItemCount();
        }
        return childCount;
    }

    /**
     * 判断是否最后一列
     * @param pos
     * @param spanCount
     * @return
     */
    private boolean isLastCol(int pos, int spanCount) {
        if ((pos + 1) % spanCount == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否最后一行
     * @param pos
     * @param spanCount
     * @param childCount
     * @return
     */
    private boolean isLastRow(int pos, int spanCount, int childCount) {
        int lastCompletedCount;
        if (childCount % spanCount == 0){
            lastCompletedCount = (childCount / spanCount - 1) * spanCount;
        } else {
            lastCompletedCount = childCount - childCount % spanCount;
        }
        if (pos >= lastCompletedCount)
            return true;
        return false;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int spanCount = getSpanCount(parent);
        int childCount = getChildCount(parent);
        int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();

        int right = mDivider.getIntrinsicWidth();
        int bottom = mDivider.getIntrinsicHeight();
        //最后一行bottom不用偏移
        if (isLastRow(itemPosition, spanCount, childCount)){
            bottom = 0;
        }
        //最后一列right不用偏移
        if (isLastCol(itemPosition, spanCount)){
            right = 0;
        }
        outRect.set(0, 0, right, bottom);
    }
}

recycleview 如何判断item是否完成显示 recyclerview获取item的高度_ide

对于分割线颜色和大小:

可以像 DividerItemDecoration 那样,使用了默认的l分割线。 实际就是颜色是#DCDCDC,大小宽高都是2px的分割线。

也可以自定义,需要写一个 drawable,比如

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#32f2" />

    <size
        android:width="1dp"
        android:height="1dp" />

</shape>

填对应的了颜色和宽高值。宽代表竖分割线的宽度,高代表横分割线的宽度。

对于 getItemOffsets 的偏移:

outRect.set(0, 0, right, bottom);是用一个Rect类来存放四个方向的偏移量。

然后会在 LayoutManager 和 RecyclerView 中的测量 measure 和放置 layout 每个 Item 逻辑里用到(不同LayoutManager用到的地方不一样)。用以扩大整个item的区域,好给之后的draw方法有可绘画的地方。

如果设置的是Vertical方向滚动。那就是在左右方向的outRect会是Item的向内收缩。上下方向是Item的向外扩张。