RecycleView中的ItemDecoration解析

/ 0评 / 4

我们知道想要给RecycleView添加一条分割线需要使用 mRecycleView.addItemDecoration()添加,Android为我们提供了类似于ListView默认分割线的DividerItemDecoration,我们将其添加到RecycleView即可得到分割线,下面就参考DividerItemDecoration来介绍下ItemDecoration。

public abstract static class ItemDecoration {
    public ItemDecoration() {
    }

    /**
     * 绘制RecycleView的背景
     */
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.onDraw(c, parent);
    }

    /**
     * 绘制RecycleView的前景
     */
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.onDrawOver(c, parent);
    }

    /**
     * 用于设置每一个item的偏移,每一个方向的偏移设置在outRect中
     */
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        this.getItemOffsets(outRect, ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
    }
}

我们只需要继承ItemDecoration即可实现自己的分割线。getItemOffsets用与设置每一个item的偏移。如下所示

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    //item的左边会有30px的距离
    outRect.left = 30;
    //item的左上右下的距离分别为 10 15 20 30px
    //outRect.set(10,15,20,30);
}

outRect的上下左右会被当做item四周的间距。

onDraw方法用于绘制RecycleView的背景,我们需要在这个方法里面绘制我们需要绘制的东西。要想绘制分隔线,那么我们需要定位到分隔线所在的位置,错误示范。

public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.onDraw(c, parent, state);
    //这会导致整个RecycleView的背景变为红色
    c.drawColor(Color.RED);
}

要想准确的绘制,我们需要找到item的边界以及其他一些信息获取Item的position

/获取当前item的position
parent.getChildAdapterPosition(view);
parent.findViewHolderForAdapterPosition(view);

//通过LayoutParams获取当前item的position
//其实我们通过LayoutParams还可以获取很多其他的信息
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.getViewLayoutPosition();

//使用LayoutManager
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int itemCount = layoutManager.getItemCount();

获取Item中设置的布局占用的区域

public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.onDraw(c, parent, state);
    int childCount = parent.getChildCount();
    for (int i = 0; i < childCount; i++) {
        //获取到子View
        View childAt = parent.getChildAt(i);
        //获取item的区域
        parent.getDecoratedBoundsWithMargins(childAt, outBounds);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        //绘制红色
        c.drawRect(outBounds, paint);

        paint.setColor(Color.BLUE);
        //直接获取item里面布局的区域,并绘制蓝色
        c.drawRect(childAt.getLeft(),childAt.getTop(),childAt.getRight(),childAt.getBottom(),paint);
    }
}

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
    //item的左上右下的距离分别为 10 15 20 30px
    outRect.set(10,15,20,30);
}

通过上图可以看到我们在getItemOffsets中设置的边距生效了,并且每一个item的区域以及我们自己的布局所占用的空间也标示出来了。我们想绘制分隔线或者其他的一些什么就可以了自由发挥了,可以参考DividerItemDecoration的源码。不过要注意的是我们在onDraw中可以绘制整个RecycleView的背景,这点我们可以用来做时光轴onDrawOver是用来绘制RecycleView的前景的,就是说会绘制在Item的上面,我们可以用来绘制徽章等。

时光轴效果

private RecyclerView.ItemDecoration getItemDecoration() {
    return new RecyclerView.ItemDecoration() {

        private Paint paint = new Paint();

        private int LineWidth = 80;

        @Override
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.onDraw(c, parent, state);
            int viewCount = parent.getChildCount();
            drawLine(c, LineWidth / 2, 0, LineWidth / 2, parent.getMeasuredHeight());
            for (int i = 0; i < viewCount; i++) {
                View child = parent.getChildAt(i);
                drawDot(c, LineWidth / 2, child.getBottom() - child.getHeight() / 2, 10);
            }
        }

        private void drawLine(Canvas c, int x1, int y1, int x2, int y2) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(2);
            paint.setColor(Color.CYAN);
            c.drawLine(x1, y1, x2, y2, paint);
        }

        private void drawDot(Canvas c, int x, int y, int width) {
            paint.setStyle(Paint.Style.FILL_AND_STROKE);
            paint.setColor(Color.RED);
            c.drawCircle(x, y, width, paint);
        }

        @Override
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
        }

        @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            //左边80px的区域用来绘制时光轴
            outRect.left = LineWidth;
        }
    };
}

给RecycleView加上间距

垂直方向加上边距

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {

    private int mSpace = 0;

    public SpacesItemDecoration(int space) {
        mSpace = space;
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (parent.getAdapter() == null) {
            return;
        }
        int childLayoutPosition = parent.getChildLayoutPosition(view);
        int itemCount = parent.getAdapter().getItemCount();
        if (itemCount < 2) {
            return;
        }
        if (childLayoutPosition == 0) {
            outRect.bottom = mSpace / 2;
        } else if (childLayoutPosition == itemCount - 1) {
            outRect.top = mSpace / 2;
        } else {
            outRect.bottom = mSpace / 2;
            outRect.top = mSpace / 2;
        }
    }
}

Grid布局

public class GridSpacesItemDecoration extends RecyclerView.ItemDecoration {

    private int mSpaceOrientation;

    private int mSpaceVertical;

    public GridSpacesItemDecoration(int spaceOrientation, int spaceVertical) {
        mSpaceOrientation = spaceOrientation;
        mSpaceVertical = spaceVertical;
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (parent.getAdapter() == null) {
            return;
        }
        if (!(parent.getLayoutManager() instanceof GridLayoutManager)) {
            throw new IllegalArgumentException(only support GridLayoutManager);
        }
        GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
        //一行多少个
        int spanCount = layoutManager.getSpanCount();
        //一共有多少个
        int itemCount = parent.getAdapter().getItemCount();
        //子布局所在的位置
        int position = parent.getChildAdapterPosition(view);

        //一共多少行 1开始
        int rowCount = itemCount / spanCount;
        //如果有不满一行的算一行
        if (itemCount % spanCount > 0) {
            rowCount = rowCount + 1;
        }

        //在第几行  0开始
        int itemRowIndex = position / spanCount;
        if (position - (itemRowIndex + 1) * spanCount > 0) {
            itemRowIndex = itemRowIndex + 1;
        }
        //在第几列 0开始
        int itemColumnIndex = position % spanCount;

        //大于一列才有左右间隔
        if (spanCount > 1) {
            if (itemColumnIndex == 0) {
                outRect.right = mSpaceOrientation / 2;
            } else if (itemColumnIndex == spanCount - 1) {
                outRect.left = mSpaceOrientation / 2;
            } else {
                outRect.right = mSpaceOrientation / 2;
                outRect.left = mSpaceOrientation / 2;
            }
        }

        //大于一行才有上下间隔
        if (rowCount > 1) {
            if (itemRowIndex == 0) {
                outRect.bottom = mSpaceVertical / 2;
            } else if (itemRowIndex == rowCount - 1) {
                outRect.top = mSpaceVertical / 2;
            } else {
                outRect.top = mSpaceVertical / 2;
                outRect.bottom = mSpaceVertical / 2;
            }
        }
    }
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注