欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于RecyclerView的ItemDecoration

程序员文章站 2022-05-31 10:54:56
...

 

之前用 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);
    }
}

 

 

 

 

 

 

关于RecyclerView的ItemDecoration

 

 

 

对于分割线颜色和大小:

可以像 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的向外扩张。

 

 

相关标签: ItemDecoration