给Grid方式排列的RecyclerView添加间距

给Grid方式排列的RecyclerView添加间距

    现在需要排列一些小卡片,每行4个,从上往下排。卡片之间水平竖直间距30px。
    毫无疑问,用的RecyclerView加GridLayoutManager。RecyclerView功能强大好用,怎么就是没有自带divider功能呢。
查找了一些资料,发现都是通过RecyclerView.addItemDecoration(RecyclerView.ItemDecoration decor)这个方法实现的。
由于我只是需要实现一个间距,所以,我只需实现重载getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)方法就可以了。
最初,我只是这么实现的:
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        //不是第一个的格子都设一个左边和底部的间距
        outRect.left = space;
        outRect.bottom = space;
        //由于每行都只有column个,所以第一个都是column的倍数,把左边距设为0
        if (parent.getChildLayoutPosition(view) %column==0) {
            outRect.left = 0;
        }
    }

也就是,除了第一列,每一列的item都margin_left=space。然后,我就发现了个问题,这样做,会导致第一列的item比右侧的item大。item的大小加上边距的总大小是相等的,这样视觉上就很难看。同理,如果除了最后一列,每一列的item都margin_right=space,则最右边的item会比左边的item大。
然后我就想到了,平分间距到每个item上,这样他们的实际大小就一样了,具体分到每个item多少边距,则可以计算出来。

@Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        /*
         * 除了第一行,每一行的每个item,距离顶部的item距离值space
         * 水平间距略复杂:对于grid方式垂直滚动的recyclerview,假设有4列
         * 排列如下:
         * ———————————————————————————————————————————
         * |    1    | |   2   | |   3    | |   4    |
         * |    5    | |   6   | |   7    | |   8    |
         * -------------------------------------------
         * 如果给不是第一列的设置左边距,第一列的会比其他item宽一个边距
         * 如果给不是最后一列的设置有右边距,同理,最后一列比其他item宽一个边距
         * 也就是说,边距也是item的一部分,所以,这种方法,会导致四个item 不一样宽
         *
         * 下面的方法,通过算法,让每一列的所有item平均分担边距
         * 对于 column = n 的grid,假设两个item间距是M:水平方向的内边距为:(n-1)*M
         * 平均到每一个item后,平均是:A=(n-1)*M/n
         * 则有如下规律:
         * 1、第一列item的左边距为零,所以右边距只能是A
         * 2、相邻左itemL,和右itemR,的左右边距和等于一个间距M
         * 类推:
         *      L       R
         *   0  0       A
         *   1  M-A     A-(M-A)
         *   2  2(M-A)  A-2(M-A)
         *   3  3(M-A)  A-3(M-A)
         *   ...
         *   n  n(M-A)  A-n(M-A)
         *   n<=column
         */
        outRect.top = space;
        int pos = parent.getChildLayoutPosition(view);
        int total = parent.getChildCount();
        if (isFirstRow(pos)) {
            outRect.top = 0;
        }
        if (isLastRow(pos, total)) {
            outRect.bottom = 5;
        }
        if (column != DEFAULT_COLUMN) {
            float avg = (column - 1) * space * 1.0f / column;
            outRect.left = (int) (pos%column * (space - avg));
            outRect.right = (int) (avg - (pos%column * (space - avg)));
        }
    }

    boolean isFirstRow(int pos) {
        return pos < column;
    }

    boolean isLastRow(int pos, int total) {
        return total - pos <= column;
    }

注释写的很清楚了。
下面是完整代码:

import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Created by zwx on 17-4-7
 * RecyclerView 以Grid方式垂直排列,每一项item都是一个小卡片(cardview)
 * 用于在item之间产生间距
 */
public class SpaceItemDecoration extends RecyclerView.ItemDecoration {

    private static final int DEFAULT_COLUMN = Integer.MAX_VALUE;
    private int space;
    private int column;

    public SpaceItemDecoration(int space) {
        this(space, DEFAULT_COLUMN);
    }

    public SpaceItemDecoration(int space, int column) {
        this.space = space;
        this.column = column;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        /*
         * 除了第一行,每一行的每个item,距离顶部的item距离值space
         * 水平间距略复杂:对于grid方式垂直滚动的recyclerview,假设有4列
         * 排列如下:
         * ———————————————————————————————————————————
         * |    1    | |   2   | |   3    | |   4    |
         * |    5    | |   6   | |   7    | |   8    |
         * -------------------------------------------
         * 如果给不是第一列的设置左边距,第一列的会比其他item宽一个边距
         * 如果给不是最后一列的设置有白牛局,同理,最后一列比其他item宽一个边距
         * 也就是说,边距也是item的一部分,所以,这种方法,会导致四个item 不一样宽
         *
         * 下面的方法,通过算法,让每一列的所有item平均分担边距
         * 对于 column = n 的grid,假设两个item间距是M:水平方向的内边距为:(n-1)*M
         * 平均到每一个item后,平均是:A=(n-1)*M/n
         * 则有如下规律:
         * 1、第一列item的左边距为零,所以右边距只能是A
         * 2、相邻左itemL,和右itemR,的左右边距和等于一个间距M
         * 类推:
         *      L       R
         *   0  0       A
         *   1  M-A     A-(M-A)
         *   2  2(M-A)  A-2(M-A)
         *   3  3(M-A)  A-3(M-A)
         *   ...
         *   n  n(M-A)  A-n(M-A)
         *   n<=column
         */
        outRect.top = space;
        int pos = parent.getChildLayoutPosition(view);
        int total = parent.getChildCount();
        if (isFirstRow(pos)) {
            outRect.top = 0;
        }
        if (isLastRow(pos, total)) {
            outRect.bottom = 5;
        }
        if (column != DEFAULT_COLUMN) {
            float avg = (column - 1) * space * 1.0f / column;
            outRect.left = (int) (pos%column * (space - avg));
            outRect.right = (int) (avg - (pos%column * (space - avg)));
        }
    }

    boolean isFirstRow(int pos) {
        return pos < column;
    }

    boolean isLastRow(int pos, int total) {
        return total - pos <= column;
    }

    boolean isFirstColumn(int pos) {
        return pos % column == 0;
    }

    boolean isSecondColumn(int pos) {
        return isFirstColumn(pos - 1);
    }

    boolean isEndColumn(int pos) {
        return isFirstColumn(pos + 1);
    }

    boolean isNearEndColumn(int pos) {
        return isEndColumn(pos + 1);
    }

}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值