keywords: GridLayoutManager, ItemDecoration, RecyclerView, item, width.
我在使用 RecyclerView 配合 GridLayoutManger 來排版 item 的時候,ui 上出現了一點棘手的問題。我需要讓 item 的水平方向的間隔出現在中間,而不是 GridLayoutManager 默認的樣子。即,如果一行 3 個 item,那么第一個和第三個需要分別與 RecyclerView 的左邊和 RecyclerView 的右邊對齊,讓留白出現在第一個、第二個 item 之間以及第二個、第三個 item 之間。但是,GridLayoutManager 默認行為是每一個 item 右邊都有一個小留白。第一個 item 與 RecyclerView 的左邊對齊,第三個 item 與 RecyclerView 的右邊也有一個留白(本文的目標就是想去掉這個留白)。
一圖以蔽之:
默認是這樣的,注意空白的位置:
我們想達到的效果是這樣的:
解決方法自然是利用 ItemDecoration 。不過這其中有些小地方還挺有意思的。
首先,不妨假設 item 的寬度一樣,并且 itemWidth * itemCountInOneRow 小于 RecyclerView 的可用寬度。那么在這種情況下,GridLayoutManager 在排版 item 的時候,會將 RecyclerView 的寬度除以 itemCountInOneRow 作為其分配給每一個 item 的可用寬度。然后我們就得到了圖1的效果。
要想完成我們的需求,需要借助 ItemDecoration。我們需要計算出,所有留白的總寬度,讓后除以 (itemCountInOneRow - 1)。這里的(itemCountInOneRow - 1)就是間隔的數目。得到的結果就是平均每個 item 需要保留的留白的寬度。然后,我們需要根據 item 的位置,指定其留白的具體位置(top、right、bottom、left)。具體邏輯如下面代碼片段所示:
//(language: kotlin)
//(可以理解成 java 的 switch,效果是一樣的)
val indexInRow = index % columnCount
when (indexInRow) {
0 -> {
outRect?.right = spaceForOneItem
}
columnCount - 1 -> {
outRect?.left = spaceForOneItem
}
else -> {
outRect?.left = spaceForOneItem / 2
outRect?.right = spaceForOneItem / 2
}
}
這里需要注意,每一個 item 的留白是相同的,計算方式跟剛才我們描述的一樣。不要試圖去讓每個 item 的留白寬度不同。我們需要做的只是去指定留白的位置。**我們需要保證的是,第一個 item 的 outRect.right
= 最后一個 item 的 outRect.left
= 中間的 item 的 outRect.left + outRect.right
**