为 GridLayout 的 RecyclerView 设置 item 间距,实现所有 Item 靠边对齐,中间留白的效果

效果图

今天项目中要实现这样的一个列表效果,因为是列表,那么当然要使用 RecyclerView ,可以轻松实现 GridLayout 的列表效果,但是有一个问题就是 RecyclerView 的 Item 样式不好写,因为 UI 上是要求所有的 Item 靠边显示,中间留出一点空白。

一开始并没有什么思路,很是头疼了一阵,最后多方查找搜索,终于找到了解决方法。办法就是给 RecyclerView 设置ItemDecoration ,从而达到 UI 效果。具体做法就是继承 RecyclerView.ItemDecoration 自定义一 ItemDecoration 代码如下:

public abstract class GridDecoration extends RecyclerView.ItemDecoration {

    private Paint mPaint;
    private int   lineWidth;//px 分割线宽

    public GridDecoration(Context context, float lineWidthDp, @ColorInt int colorRGB) {
        this.lineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, lineWidthDp, context.getResources().getDisplayMetrics());
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(colorRGB);
        mPaint.setStyle(Paint.Style.FILL);
    }

    public GridDecoration(Context context, int lineWidthDp, @ColorInt int colorRGB) {
        this(context, (float) lineWidthDp, colorRGB);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        //left, top, right, bottom
        int childCount1 = parent.getChildCount();
        //        int childCount2 = parent.getLayoutManager().getChildCount();
        //        int childCount3 = parent.getAdapter().getItemCount();
        //        Log.e("count", "getChildCount()=" + childCount1 + "-----getLayoutManager().getChildCount()=" + childCount2 + "----getAdapter().getItemCount()=" + childCount3);
        for (int i = 0; i < childCount1; i++) {
            View child = parent.getChildAt(i);

            int itemPosition = ((RecyclerView.LayoutParams) child.getLayoutParams()).getViewLayoutPosition();

            boolean[] sideOffsetBooleans = getItemSidesIsHaveOffsets(itemPosition);
            if (sideOffsetBooleans[0]) {
                drawChildLeftVertical(child, c, parent);
            }
            if (sideOffsetBooleans[1]) {
                drawChildTopHorizontal(child, c, parent);
            }
            if (sideOffsetBooleans[2]) {
                drawChildRightVertical(child, c, parent);
            }
            if (sideOffsetBooleans[3]) {
                drawChildBottomHorizontal(child, c, parent);
            }
        }
    }

    private void drawChildBottomHorizontal(View child, Canvas c, RecyclerView parent) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        int left = child.getLeft() - params.leftMargin - lineWidth;
        int right = child.getRight() + params.rightMargin + lineWidth;
        int top = child.getBottom() + params.bottomMargin;
        int bottom = top + lineWidth;

        c.drawRect(left, top, right, bottom, mPaint);
    }

    private void drawChildTopHorizontal(View child, Canvas c, RecyclerView parent) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        int left = child.getLeft() - params.leftMargin - lineWidth;
        int right = child.getRight() + params.rightMargin + lineWidth;
        int bottom = child.getTop() - params.topMargin;
        int top = bottom - lineWidth;

        c.drawRect(left, top, right, bottom, mPaint);
    }

    private void drawChildLeftVertical(View child, Canvas c, RecyclerView parent) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        int top = child.getTop() - params.topMargin - lineWidth;
        int bottom = child.getBottom() + params.bottomMargin + lineWidth;
        int right = child.getLeft() - params.leftMargin;
        int left = right - lineWidth;

        c.drawRect(left, top, right, bottom, mPaint);
    }

    private void drawChildRightVertical(View child, Canvas c, RecyclerView parent) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        int top = child.getTop() - params.topMargin - lineWidth;
        int bottom = child.getBottom() + params.bottomMargin + lineWidth;
        int left = child.getRight() + params.rightMargin;
        int right = left + lineWidth;

        c.drawRect(left, top, right, bottom, mPaint);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        //outRect 看源码可知这里只是把Rect类型的outRect作为一个封装了left,right,top,bottom的数据结构,
        //作为传递left,right,top,bottom的偏移值来用的
        int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        boolean[] sideOffsetBooleans = getItemSidesIsHaveOffsets(itemPosition);

        //如果是设置左边或者右边的边距,就只设置成指定宽度的一半,
        // 因为这个项目中的 Grid 是一行二列,如果不除以二的话,那么中间的间距就会很宽,
        //可根据实际项目需要修改成合适的值
        int left = sideOffsetBooleans[0] ? lineWidth/2 : 0;
        int top = sideOffsetBooleans[1] ? lineWidth : 0;
        int right = sideOffsetBooleans[2] ? lineWidth/2 : 0;
        int bottom = sideOffsetBooleans[3] ? lineWidth : 0;

        outRect.set(left, top, right, bottom);
    }

    /**
     * 顺序:left, top, right, bottom
     *
     * @return boolean[4]
     */
    public abstract boolean[] getItemSidesIsHaveOffsets(int itemPosition);


}

使用也很简单,用 RecyclerView 的 addItemDecoration() 方法添加 decoration ,需要传入三个参数,分别为:context,要设置的边距宽度(单位:dp),边距颜色。

mRecyclerView.setLayoutManager(new GridLayoutManager(_mActivity, 2));
        int color = getResources().getColor(R.color.gray);
        mRecyclerView.addItemDecoration(new GridDecoration(_mActivity, 8, color) {
            @Override
            public boolean[] getItemSidesIsHaveOffsets(int itemPosition) {
                //顺序:left, top, right, bottom
                boolean[] booleans = {false, false, false, false};
                if (itemPosition == 0) {
                    //因为给 RecyclerView 添加了 header,所以原本的 position 发生了变化
                    //position 为 0 的地方实际上是 header,真正的列表 position 从 1 开始
                } else {
                    switch (itemPosition % 2) {
                        case 0:
                            //每一行第二个只显示左边距和下边距
                            booleans[0] = true;
                            booleans[3] = true;
                            break;
                        case 1:
                            //每一行第一个显示右边距和下边距
                            booleans[2] = true;
                            booleans[3] = true;
                            break;
                    }
                }
                return booleans;
            }
        });

上面的代码中因为我是给 RecyclerView 设置了一个 headView,所以 position 为 0 的时候实际上是 headView,真正 RecyclerView 的 item 位置是从 1 开始的,注意这一点即可。

还有就是我的项目中是每行两列的,如果是每行三列或者更多,需要稍微改下 GridDecoration ,以适配自己的项目,当然也可以进一步封装和完善。

如果有朋友在此基础上封装了更好更完善的 ItemDecoration ,请不吝赐教,可以留言或 Github 提 issue ,谢谢!

Demo 地址 点这里

如果对您有帮助,欢迎 star 一个。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要动态设置 RecyclerViewitem 的边距,你可以创建一个自定义的 ItemDecoration,并在其中设置边距。以下是一个示例代码,展示了如何动态设置 RecyclerViewitem 的边距: 首先,创建一个自定义的 ItemDecoration 类,例如 CustomItemDecoration: ```java public class CustomItemDecoration extends RecyclerView.ItemDecoration { private int spacing; public CustomItemDecoration(int spacing) { this.spacing = spacing; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); // 设置边距 outRect.left = spacing; outRect.right = spacing; outRect.top = spacing; outRect.bottom = spacing; } } ``` 然后,在你的 Activity 或 Fragment 中,通过调用 RecyclerView 的 addItemDecoration 方法来应用这个自定义的 ItemDecoration: ```java int spacing = getResources().getDimensionPixelSize(R.dimen.item_spacing); // 获取边距的像素值 RecyclerView recyclerView = findViewById(R.id.recyclerView); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); RecyclerView.Adapter adapter = new YourAdapter(); recyclerView.setAdapter(adapter); recyclerView.addItemDecoration(new CustomItemDecoration(spacing)); ``` 在上述示例中,我们通过 getResources().getDimensionPixelSize(R.dimen.item_spacing) 方法获取了边距的像素值。你可以在 res/values/dimens.xml 文件中定义这个边距的尺寸。 最后,我们创建了一个 LinearLayoutManager,并将其设置RecyclerView 的布局管理器。然后,设置了适配器,并通过 addItemDecoration 方法将自定义的 ItemDecoration 应用到 RecyclerView 上。 这样就可以动态设置 RecyclerViewitem 的边距了。希望对你有所帮助!
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值