眾所周知,要給RecyclerView中的Item加間距,有兩種比較通用的方式:
- 直接在item的布局中加入間距
- 給RecyclerView添加ItemDecoration來處理間距
第一種方式很直白,就是每個(gè)item之間其實(shí)沒有間距,是通過在每個(gè)item內(nèi)部處理顯示內(nèi)容和邊界處手動加空白,這樣看起來就像是有了間距一樣。而第二種方式,表面看起來也是很簡單的:間距多少直接填多少就好了嘛,有什么難的。但是實(shí)際使用時(shí)呢,會有奇怪的問題。
舉個(gè)栗子
一個(gè)簡單的需求,要給一個(gè)N行3列的RecyclerView加間距,每個(gè)Item之間間距10,然后靠邊的距離屏幕邊距是0,即緊貼屏幕,這樣寫:
addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val pos = parent.getChildAdapterPosition(view)
val column = (pos % span)
outRect.left = 5
outRect.right = 5
outRect.top = 5
outRect.bottom = 5
if (column == 0){
outRect.left = 0
}
if (column == 2){
outRect.right = 0
}
}
})
看起來好像沒問題?第一列的左邊距都是0,第三列的右邊距都是0,除此之外邊所有Item的左右間距剛好是 5+5=10,是這樣嗎?看看效果

很明顯,中間的item變小了,這是ItemDecoration繪制原理上導(dǎo)致的。因?yàn)镮tem首先是均等分布,且Item內(nèi)部顯示的內(nèi)容部分并不是完全居中,然后繪制計(jì)算的這個(gè)過程,又是從左向右一一計(jì)算的。所以越往后計(jì)算會把距離拉大,雖然看起來間距是正常的,但是因?yàn)榭娠@示內(nèi)容部分的寬度被擠壓了,所以顯得中間部分可顯示區(qū)域變小了。
怎么解決呢?也很簡單,在這個(gè)例子中,對于每一行來說,只有兩部分間距,即第一列的Item和第二列的Item間有一個(gè)間距,第二列Item跟第三列Item之間也有一個(gè)間距。而左右兩側(cè)距離屏幕距離是0,所以得出這一行中除了可顯示內(nèi)容View之外,剩下的需要留白的寬度是10+10=20。然后,因?yàn)橐恍惺侨?,所以對于每一個(gè)Item來說,他所分得的空白寬度,就是20/3。那么對第一列Item來說,他的左側(cè)距離是0(靠左側(cè)屏幕),那么右側(cè)距離就是 20/3-0=20/3。第二列,他的左側(cè)距離是多少呢?想一下,他的左側(cè)距離再加上第一列Item的右側(cè)距離,剛好就是10,既然已經(jīng)知道了第一列Item的右側(cè)間距,那么第二列Item的左側(cè)距離就是10-20/3=10/3,那他這個(gè)Item當(dāng)前還剩下的空白距離,就是20/3-10/3,也就是10/3。那第三列Item的左側(cè)邊距,同理可得為10-10/3=20/3,右側(cè)距離為20/3-20/3,剛好是0,代碼如下:
addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val pos = parent.getChildAdapterPosition(view)
val column = (pos % span)
if (column == 0) { //第一列
outRect.left = 0
outRect.right = 20/3
} else if (column == 1) { //第二列
outRect.left = 10/3
outRect.right = 10/3
} else { //第三列
outRect.left = 20/3
outRect.right = 0
}
outRect.top = 5
outRect.bottom = 5
}
})
這樣效果就完美了:
