最近使用 RecyclerView + GridLayoutManager 时,需要 使用 ItemDecoration 画分割块, 当分割块的宽高稍微大一点之后 ,发现 各个 item的宽高不相同。
我也参考过网上 很多 讲解 ItemDecoration 的例子, 这些例子都太片面, 但这也很正常, 它只是要教会你 ItemDecoration的基本用法,能实现一个差不多功能就行。 这些例子 大多数 把 分割块 的宽度 设置成 1px 或 1dp 到 3dp, 导致 根本看不出 各个 item的宽度 不一致。
大名鼎鼎的鸿洋大神的那篇csdn博客画gridLayoutManager分割线,当把 分割线宽度设置成 30dp时,明显发现 最后一个 item的 内容宽度 变大了, 因此也有问题。不信的可自己去试试。
getItemOffsets(Rect outRect , xxxxxxxxx) 方法里的 Rect 实际上相当于给 itemView 设置了一个 Margin, 但 itemView的 最大宽高 是 屏幕宽度的 spanCount分之一。
那如何 实现一个 自适应 任意列数的 ItemDecoration, 这里提供一个 很 巧妙的 实现方式, 源码如下:
/** * 自适应任意列的 GridLayoutManager 的分割块 ItemDecoration * Created by meikai on 2017/11/07. */ public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int dividerWidth; private int dividerWidthTop; private int dividerWidthBot; private Paint dividerPaint; /** * @param spanCount gridLayoutManager 列数 * @param dividerWidthDp 分割块宽高,单位:dp */ public GridSpacingItemDecoration(Context context, int spanCount, int dividerWidthDp) { this.spanCount = spanCount; this.dividerPaint = new Paint(); this.dividerPaint.setColor(Color.BLUE); this.dividerWidth = DensityUtil.dp2px(context, dividerWidthDp); this.dividerWidthTop = dividerWidth / 2; this.dividerWidthBot = dividerWidth - dividerWidthTop; } @Override public void getItemOffsets(Rect outRect, View child, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, child, parent, state); int pos = parent.getChildAdapterPosition(child); int column = (pos) % spanCount;// 计算这个child 处于第几列 outRect.top = dividerWidthTop; outRect.bottom = dividerWidthBot; outRect.left = (column * dividerWidth / spanCount); outRect.right = dividerWidth - (column + 1) * dividerWidth / spanCount; Log.e("getItemOffsets", "pos=" + pos + ", column=" + column + " , left=" + outRect.left + ", right=" + outRect.right + ", dividerWidth=" + dividerWidth); } }
这个算法 精妙的 地方 在 这两句 :
outRect.left = (column * dividerWidth / spanCount); outRect.right = dividerWidth - (column + 1) * dividerWidth / spanCount;
任意一个 child 的 left 值 跟它的 列数关联起来, 这个 child 的 right = 分割块的宽度 减去 下一列的left 。
结论:分割块的实际宽度 = 上一个child的right + 下一个child的 left.
例如:以5列显示的 gridLayoutManager 为例,要求 最左边和最右边 无分割块,中间每一个分割块的宽度相同, 则枚举每一个item的左右边距的值如下:
左边距 右边距
第1列 0/5 4/5
第2列 1/5 3/5
第3列 2/5 2/5
第4列 3/5 1/5
第5列 4/5 0/5
其中 第1列的右边距 + 第2列的左边距 = 4/5 + 1/5 = 1个单位的 分割块宽度。
至于如何推导出这个算法,我也不得而知, 是最近在 stackoverflow 上找到的,我想应该是有个 推导过去, 最主要的思想是把 left 和 right 的值 与 列索引 关联起来。这样就能自适应任意列数的 gridLayoutManager 了。
下面是 stack overflow 的 链接:
https://stackoverflow.com/questions/28531996/android-recyclerview-gridlayoutmanager-column-spacing