RecycleView ItemDecoration解析之getItemOffsets

1,088 阅读2分钟
导读

    我们常常遇到设计要求RecycleView的自定义item的左右边距,中间均分,这时我们就需要自定义ItemDecoration,实现getItemOffsets方法,如何计算呢?

预备知识
  • ItemDecoration解析(一) getItemOffsets
  • 上条源码解析也可以略过,GridLayoutManager为了保证每个itemView在水平方向(orientation为vertical时)或者垂直方向(orientation为horizontal时)均分,那么必须让每个itemview的paddingleft + paddingRight ( orientation 为vertical 时 ) 或者 paddingTop + paddingBottom(orientation 为horizontal时)相等,当orientation为vertical时,我们需要在getItemOffsets方法中计算每个Item的PaddingLeft,以及PaddingRight,保证每个Item的paddingLeft+paddingRight相等,这样才能达到均分的目的。
实例分析
假如间距(space )= 14, spanCount = 4, 边距(edgeSpace)= 0
totalSpace = space * (itemCount-1) + edgeSpace * 2 = 42 // space总和
eachSpace = totalSpace / itemCount  = 10.5 // item的leftPadding+rightPadding的和列出每一列的paddingLeft以及paddingRight:

colunm	    Left	        Right
0	    edgeSpace(0)	eachSpace-L0(10.5)
1	    space-R0(3.5)	eachSpace-L1 (7)
2	    space-R1(7)	    eachSpace-R2(3.5)
3	    space-R2(10.5)	edgeSpace(0)
  • 结论:Left是从0到eachSpace等差数列;Right用eachSpace-Left算出。
假如间距(space )= 14, spanCount = 4, 边距(edgeSpace)= 0
totalSpace = space * (itemCount-1) + edgeSpace * 2 = 66  //space总和
eachSpace = totalSpace / itemCount= 16.5  // item的leftPadding+rightPadding的和列出每一列的paddingLeft以及paddingRight:

colunm	    Left	        Right
0	    edgeSpace(12)	eachSpace-L0(4.5)
1	    space-R0(9.5)	eachSpace-L1 (7)
2	    space-R1(7)	    eachSpace-R2(9.5)
3	    space-R2(4.5)	edgeSpace(12)
  • 结论:Left是从edgeSpace到(eachSpace - edgeSpace)等差数列;Right用eachSpace-Left算出。
计算
  • paddingLift是等差数列,公差计算公式:
1. 当边距为0时,d = eachSpace / (spanCount - 1);
2. 当边距不为0时,d = ( eachSpace - edgeSpace - edgeSpace ) / (spanCount - 1);
代码
public class BookAndProgramCoverItemDecoration implements ItemDecoration {

	private int mEdgeSpace;//左右间距
	private int mSpanCount;//列数
	private int mEachSpace;//平均每个item可用间距(减去item自身宽度)
	private int d;//公差

	public BookAndProgramCoverItemDecoration(Context context, int spanCount) {
		this(spanCount, Utils.dip2px(context, 15), Utils.getDeviceWidthPixels(context) - spanCount * CoverUtils.getBookCoverWidth(context));
	}

	public BookAndProgramCoverItemDecoration(int mSpanCount, int mEdgeSpace, int mTotalSpace) {
		this.mSpanCount = mSpanCount;
		this.mEdgeSpace = mEdgeSpace;
		this.mEachSpace = mTotalSpace / mSpanCount;

		d = (mEachSpace - mEdgeSpace - mEdgeSpace) / (mSpanCount - 1);
	}

	@Override
	public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
		int column = itemPosition % mSpanCount;
		outRect.left = column * d + mEdgeSpace;
		outRect.right = mEachSpace - outRect.left;
	}
}
/**
 * Discription: RecyclerView均分,边距请设置padding
 * Created by  on 2020/5/8.
 */
public class SpaceItemDecoration extends RecyclerView.ItemDecoration {
    private final int column;
    private final int space;
    private final int top;

    /**
     * Created by  on 2020/5/8.
     * Description: https://www.jianshu.com/p/b23e43f461c0 减掉RecyclerView父布局两侧padding和item的宽度,然后平分,默认每个item右侧会填充剩余空间
     * int space = (Utils.getDeviceWidthPixels(getContext()) - spanCount * Utils.dip2px(getContext(), 80) - 2 * Utils.dip2px(getContext(), 15)) / (spanCount * (spanCount - 1));
     * @param
     * @return
     */
    public SpaceItemDecoration(int space, int column, int top) {
        this.space = space;
        this.column = column;
        this.top = top;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        // 第一列左边贴边、后边列项依次移动一个space和前一项移动的距离之和
        int mod = parent.getChildAdapterPosition(view) % column;
        outRect.left = space * mod;
        outRect.top = top;
    }
}

结论:推荐使用SpaceItemDecoration,第一种在存在拖动排序时,item间距无法均分。

参考
  1. ItemDecoration解析(一) getItemOffsets
  2. RecyclerView添加GridLayoutManager布局间隔均分