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

来源:互联网 发布:知呱呱 靠不靠谱 编辑:程序博客网 时间:2024/06/05 20:05

给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);    }}