RecyclerView 支持網(wǎng)格布局滤淳,我們使用GridLayoutManager來設(shè)置為網(wǎng)格布局贤徒,在使用網(wǎng)格布局時(shí)遇到Item之間間隔的問題醉蚁,以下以每行三個(gè)為例來講解睛琳。
每行顯示三個(gè)圖片盒蟆,設(shè)置圖片寬度恒定(eg :100dp)后踏烙,在不設(shè)置分隔的情況下,recyclerview會(huì)自動(dòng)計(jì)算空隙寬度历等,然后填充到每個(gè)Item右邊讨惩,如下圖所示。但這種布局往往不滿足我們的使用習(xí)慣寒屯,我們一般需要使圖片左右靠邊荐捻,中間間隙自動(dòng)填充,因此我們需要移動(dòng)item的位置浩螺。如下所示靴患,需要將第二個(gè)、第三個(gè)Item右移要出。
下面開始介紹我們的實(shí)現(xiàn)方案鸳君,我們所需的工作就是計(jì)算每個(gè)item所需的移動(dòng)距離,計(jì)算的過程后面介紹患蹂,現(xiàn)在假設(shè)我們已經(jīng)計(jì)算出這個(gè)距離或颊,為dividerWidth,那么我們只需要以下代碼就ok了传于。
private final int SPAN_COUNT = 3;
gridLayoutManager = new GridLayoutManager(this, SPAN_COUNT, LinearLayoutManager.VERTICAL, false);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return SPAN_COUNT/3;
}
})囱挑;
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
GridLayoutManager.LayoutParamslayoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams();
int spanSize =layoutParams.getSpanSize();
if(spanSize ==eachItemCount){
int spanIndex =layoutParams.getSpanIndex();
int itemPosition =spanIndex/eachItemCount;//eachItemCount每個(gè)item所占空間
outRect.left =dividerWidth*itemPosition;//dividerWidth每份間隔寬度(item左側(cè)間距)下面會(huì)具體介紹.
}
outRect.bottom =dividerHeight;//item底部間距
}
設(shè)置完以上代碼就可以正常工作了,下面簡單介紹下他是怎么工作的:
outRect結(jié)構(gòu)如下所示沼溜,及矩形的數(shù)據(jù)結(jié)構(gòu)平挑,四個(gè)值分別代表目標(biāo)Item四周的距離,如下圖所示:
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;}
跟下源碼定位到以下代碼(版本V7-25.0.1):
Rect getItemDecorInsetsForChild(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
...
final Rect insets = lp.mDecorInsets;
insets.set(0, 0, 0, 0);
final int decorCount = mItemDecorations.size();//mItemDecorations為分割線的集合系草,因此GridLayout可以設(shè)置多個(gè)分隔線
for (int i = 0; i < decorCount; i++) {
mTempRect.set(0, 0, 0, 0);
//此處調(diào)用我們重寫的函數(shù)通熄,insets是Item的四周間距,此處加上我們設(shè)置的間距
mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
insets.left += mTempRect.left;
insets.top += mTempRect.top;
insets.right += mTempRect.right;
insets.bottom += mTempRect.bottom;
}
lp.mInsetsDirty = false;
return insets;
}
其實(shí)寫這篇文章的主要原因是Item移動(dòng)位置的計(jì)算找都,以下以每行三個(gè)為例計(jì)算每個(gè)item移動(dòng)的位置:
上圖中空白處和為1唇辨,則每個(gè)空白為1/3,移動(dòng)后一共兩個(gè)空白能耻,故每個(gè)占1/2赏枚,
顯然第二個(gè)Item需要右移(1/2-1/3 = 1/6)的位置,那么第三個(gè)item需要移動(dòng)自己的1/6距離+第二個(gè)item移動(dòng)的位置1/6晓猛,即2/6饿幅。因此我們在計(jì)算空白的寬度時(shí)將其分為6份。
@Px int eachDividerWidth = (UISizeUtils.getScreenWidth(this)-UISizeUtils.dip2px(this,100)*SPAN_COUNT)/6;//把空白分為6份
DividerGridLayout dividerGridLayout = new DividerGridLayout(1,eachDividerWidth,UISizeUtils.dip2px(this,10));
因此計(jì)算出每份距離后戒职,在getItemOffsets方法中根據(jù)spanIndex來計(jì)算每個(gè)Item距離左側(cè)的距離
outRect.left的距離栗恩,為dividerWidth*itemPosition。
if(spanSize ==eachItemCount){
int spanIndex =layoutParams.getSpanIndex();
int itemPosition =spanIndex/eachItemCount;//eachItemCount每個(gè)item所占空間
outRect.left =dividerWidth*itemPosition;//dividerWidth每份間隔寬度(item左側(cè)間距)
}
將其推廣到每行四個(gè)Item時(shí)帕涌,每個(gè)每個(gè)空白為1/4摄凡,移動(dòng)后每個(gè)空白占1/3,因此每個(gè)Item需要移動(dòng)(1/3-1/4 = 1/12),每行5個(gè)Item時(shí)每個(gè)移動(dòng)(1/4-1/5 = 1/20)蚓曼,歸納總結(jié)亲澡。對于每行N個(gè)Item,每個(gè)Item需要移動(dòng)(1/(N*(N-1)))的位置纫版。