為RecyclerView設(shè)置相同尺寸的分割線——不止簡單的自定義ItemDecoration

項(xiàng)目中經(jīng)常碰到列表當(dāng)中的每一項(xiàng)之間需要設(shè)置間距(分割線)的問題,對于RecyclerView來說,官方?jīng)]有提供專門的方法實(shí)現(xiàn)。因此傳統(tǒng)的辦法是,通過給列表中每一項(xiàng)的根布局設(shè)置margin值來實(shí)現(xiàn),例如需要左右間距時(shí),可以給每一項(xiàng)設(shè)置right_margin。這種方法下,對于整個(gè)列表來說,它的左邊沒有邊距,而右邊有一個(gè)right_margin大小的邊距,這樣對于列表來說UI不對稱。解決的辦法是,給整個(gè)列表設(shè)置與right_margin同樣大小的left_margin。但是,這樣的方法并不優(yōu)雅、不全面,比如如果這個(gè)列表左右兩邊不需要邊距,這樣的方法是實(shí)現(xiàn)不了的。

其實(shí),官方為RecyclerView提供了ItemDecoration的概念,我們可以通過自定義ItemDecoration為item之間設(shè)置分割線。想要item間的左右間距都相同,通常的做法也類似于上面提到的辦法,在getItemOffsets方法中給每一行的每個(gè)item的outRect設(shè)置相同的right,在這一行的最后一項(xiàng)不設(shè)置right或者給第一項(xiàng)設(shè)置left。這樣的方法,比上面的方法優(yōu)雅一些,因?yàn)榭梢耘袛嘁恍挟?dāng)中第一項(xiàng)或者最后一項(xiàng),并作區(qū)分處理。但是,在ItemDecoration方法中通過outRect設(shè)置的間距,都是占用item本身的空間,所以如果每個(gè)item設(shè)置的outRect不一樣,則item本身顯示出來的寬高也會不一樣,比如如果一行的最后一項(xiàng)沒設(shè)置right或第一項(xiàng)多設(shè)置left,則這一項(xiàng)和其他項(xiàng)寬度就不一致。所以我們來探索新的方式,使每個(gè)item的寬度維持一致并且間距也相同。

這里面需要推算一下。假設(shè)我們要處理的是縱向滾動的列表,每一行的item個(gè)數(shù)為spanCount,item間的間距(縱向)相同為space。則這一行中除去item占用的空間,剩下的空間為(假設(shè)最左邊和最右邊都有間距):

totalSpace=(spanCount+1)*space

這樣,為了保證每個(gè)item的寬度相同且間距相同,每一個(gè)item所需要提供給間距的寬度是

itemSpace = totalSpace/spanCount
= (spanCount+1)*space/spanCount
= space + space/spanCount

對于一行當(dāng)中的第一個(gè)item,左邊的間距是

left1 = space

可以計(jì)算出右邊的間距是

right1 = itemSpace - left1
= space + space/spanCount - space
= space/spanCount

對于一行當(dāng)中的第二個(gè)item,左邊的間距是

left2 = space - right1
= space - space/spanCount
= space*(spanCount - 1)/spanCount

右邊的邊距是

right2 = itemSpace - left2
= space + space/spanCount - (space - space/spanCount)
= 2*space/spanCount

對于第三個(gè)item,左邊的間距是

left3 = space - right2
= space - 2*space/spanCount
= space*(spanCount - 2)/spanCount

右邊的邊距是

right3 = itemSpace - left3
= space + space/spanCount - (space - 2*space/spanCount)
= 3*space/spanCount

這樣我們可以發(fā)現(xiàn)一些規(guī)律,假設(shè)一行當(dāng)中item的索引為spanIndex(從0開始),則對于這個(gè)item來說,左邊的間距是

left = space*(spanCount - spanIndex)/spanCount

右間距是

right = (spanIndex + 1)*space/spanCount

對于RecyclerView來說,spanIndex和spanCount都可以獲取到。

GridLayoutManager:

GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
spanIndex = lp.getSpanIndex();
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
spanCount = layoutManager.getSpanCount();

StaggeredGridLayoutManager:

StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
spanIndex = lp.getSpanIndex();
StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) parent.getLayoutManager();
spanCount = layoutManager.getSpanCount(); 

LinearLayoutManager:

spanIndex = 0;
spanCount = 1;

同理,我們可以推出很多其他情況,例如橫向排列,例如去掉最邊上的間距,例如item占據(jù)的列不止一列……

根據(jù)上面的理論,我實(shí)現(xiàn)了一個(gè)自定義ItemDecoration:SCommonItemDecoration,包含了上面及其他所有情況,可以設(shè)置橫向和縱向列表的間距,同時(shí)可以設(shè)置邊緣是否也設(shè)置間距。支持LinearLayoutManager,GridLayoutManager和StaggeredGridLayoutManager,也支持不同的orientation。同時(shí),SCommonItemDecoration還支持為不同的item type設(shè)置不同的間距。

代碼在這里: https://github.com/ibosong/CommonItemDecoration

用法

先構(gòu)建一個(gè)SCommonItemDecoration.ItemDecorationProps的SparseArray列表,

SparseArray<SCommonItemDecoration.ItemDecorationProps> propMap = new SparseArray<>();

為其添加項(xiàng)目,其中的每一項(xiàng)是一個(gè)item type對應(yīng)一個(gè)ItemDecorationProps對象,ItemDecorationProps包含屬性:

private int verticalSpace; // 縱向間距
private int horizontalSpace; // 橫向間距
private boolean hasVerticalEdge; // 是否給左右邊緣設(shè)置間距
private boolean hasHorizontalEdge; // 是否給上下邊緣設(shè)置間距

然后構(gòu)建SCommonItemDecoration對象并設(shè)置給RecyclerView

mBrandRecyclerView.addItemDecoration(new SCommonItemDecoration(propMap));

不多說了,不懂的看sample吧。

demo.gif

【轉(zhuǎn)載請注明出處】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容