最近在做需求的时候,需要在界面展示一个四列的列表。毫无疑问,我们应该采用RecyclerView并采用它的网格布局管理器,即GridLayoutManager来实现。当然,这种实现肯定是没问题的。不过设计老师一般会要求让这几列的列表把给出的宽度等距离分割掉。也就是说,第一列和最后一列的列表不能距两边有间距,之间的需要等分间距。类似这样的效果
那么假如我们把宽度设置为wrap_content,则出来的效果距离右边还有相当距离,无法让最后一列内容紧贴右侧。假如我们将宽度设置为match_parent,再给每个item右边设置间距亦或是将宽度写死,再添加margin,均无法得到预期的效果。
但是将宽度设置为match_parent是可以做到将每个Item均分,即便它们之间没有间距。这也是我们实现等间距的前提。
添加间距和RecyclerView的ItemDecoration有关,其中有一个方法getItemOffsets。这个方法获取给定ItemView的偏移量,用outRect 表示。outRect的每个字段分别表示不同方向的偏移量,类似于填充或边距。默认设置为0,即没有间距。
因此重写这个方法即为我们实现等间距的核心,实现的思路如下
左侧的间距+右侧的间距为设置的间距,在视觉上达到等分的效果。需要满足两个条件
- 各个Item的大小相等,即各列的left+right 值相等
- 各列的间距相等,即前列的right + 后列的left = 列间距
得出公式
某列的左间距 =? 所在列 * (列间距?* (列数)/ 1)
某列的右间距 = 列间距 - 后列的左间距 = 列间距 - (所在列 + 1)* (列间距?* (列数)/ 1)
所在列从0开始
下面看代码实现
class GridSpaceItemDecoration(
private val spaceCount: Int,
private val rowSpace: Float,
private val columnSpacing: Float
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view) // 获取view 在adapter中的位置。
val column = position % spaceCount // view 所在的列
outRect.left = (column * columnSpacing / spaceCount).toInt() // column * (列间距 * (1f / 列数))
outRect.right =
(columnSpacing - (column + 1) * columnSpacing / spaceCount).toInt() // 列间距 - (column + 1) * (列间距 * (1f /列数))
// 如果position > 行数,说明不是在第一行,则不指定行高,其他行的上间距为 top=rowSpace
if (position >= spaceCount) {
outRect.top = rowSpace.toInt() // item top
}
}
}
使用方法为
binding.paperContentView.addItemDecoration(
GridSpaceItemDecoration(
4,
0F,
DisplayUtil.dp2px(requireContext(), 25F)
)
)
其中DisplayUtil是dp转px的工具类,大家按自己的需求传入列间距和行间距就可以了。
文章原创来自RecyclerView GridLayoutManager 等分间距 - 掘金
|