序:前邊我因?yàn)轫?xiàng)目需要擼了一下RecyclerView GridLayoutManager item設(shè)置萬(wàn)能分隔線,感覺還是實(shí)用的吧,閱讀量也水漲船高,令人欣喜,也滿足了內(nèi)心的小九九~~~ ??
至于這篇文章呢,是在上一篇的基礎(chǔ)上做加法,增加了HeadView的顯示。所以至于Item間距啥的算法,原理之類的這里就不再講解了。沒看過(guò)上一篇文章的親們,直接先去擼一把~
下面先貼個(gè)圖:

看到這個(gè)圖大家第一想到的做法是什么?
讓我們猜猜看:是不是一個(gè)RecyclerView(LinearLayoutManager)嵌套另一個(gè)RecyclerView(GridLayoutManager)?
嗯!我們一般都是這么做的,也沒什么不妥。
但是這里呢,我們換個(gè)角度,換個(gè)思維,嘗試用給一個(gè)RecyclerView給它解決了~,年輕就是要燥??
照舊~ 無(wú)圖無(wú)真相??



一、首先,我們要對(duì)我們需要顯示的Item進(jìn)行ViewType區(qū)分
@Override
public int getItemViewType(int position) {
if(listData != null)
return listData.get(position).getViewType();
return super.getItemViewType(position);
}
大家都看得懂哈~ 略...
二、我們要對(duì)GridLayoutManager,做定制以用一行顯示HeaderView
final GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int itemViewType = subAdapter.getItemViewType(position);
if(itemViewType == Constants.PENDING_UPLOAD_SUB_VIEW_TYPE_HEADER)
return gridLayoutManager.getSpanCount();
else
return 1;
}
});
這里實(shí)際上就是獲取Item的ViewType,如果是HeadView的Type就將GridLayoutManger的spanCount,變成一行。
spanSize 是說(shuō)用多少個(gè)Item空間來(lái)顯示這個(gè)View(比如說(shuō)可以用2個(gè)Item位置顯示該View,也可以3個(gè)等最大不超過(guò)設(shè)置的SpanCount),我們這里是獲取spanCount,也就是3個(gè),相當(dāng)于一行。
三、定制適合多個(gè)ViewType的ItemDecoration
其實(shí),使用一個(gè)RecyclerView來(lái)做圖1.1的效果,最重要的是要定制適合的ItemDecoration,前面文章詳解了GridLayout Item之間平均間距的原理和實(shí)現(xiàn)方法。這里就不多說(shuō)了。
為了實(shí)現(xiàn)HeadView和子Item同在一個(gè)RecyclerView,并且能正確的設(shè)置他們之間的間距,看起來(lái)還是挺繁瑣的,我們來(lái)試試看
3.1 首先我們要在自定義的ItemDecoration中,區(qū)分HeadView和subItem
int spanCount = getSpanCount(parent);
int spanSize = getSpanSize(itemPosition,parent);
if(spanSize == spanCount){//這是有HeaderView 的情況
...
}else {//類似HeaderView 下的子item
...
}
這里我們可以通過(guò)獲取GridLayoutManager的SpanCount和item的SpanSize,如果這兩者相等就說(shuō)明是HeadView Item,因?yàn)槲覀冎霸谠O(shè)置GridLayoutManager spansize的時(shí)候設(shè)置為gridLayoutManager.getSpanCount()。
3.2 通過(guò)上一步我們能夠區(qū)分HeadView和subItem,但是仍然有個(gè)問題是:我們?nèi)绾文苤烂總€(gè)Item的相對(duì)Position?
什么意思呢?
1、比如說(shuō)我們Item的絕對(duì)position,是按照順序排列的,0、1、2、3、4....等等
(實(shí)際上就是上一篇《RecyclerView GridLayoutManager item設(shè)置萬(wàn)能分隔線》中使用到的position)
2、而我所說(shuō)的Item的相對(duì)position,有點(diǎn)難解釋
先貼個(gè)上個(gè)文章的公式
第一個(gè)Item:L0=sW R0=eW-sW
第二個(gè)Item:L1=dW-R0=dW-eW+sW R1=eW-L1=2eW-dW-sW
第三個(gè)Item:L2=dW-R1=2(dW-eW)+sW R2=eW-L2=3eW-2dW-sW
所以根據(jù)以上可以得出
Ln = (position % spanCount) * (dW-eW) + sW
Rn = eW-Ln
這里可看出我們要獲取Ln = (position % spanCount) * (dW-eW) + sW中的position,而這里的這個(gè)position就不是上篇文章那個(gè)絕對(duì)Position了。
對(duì)于HeadView的left、top、right、bottom都是要設(shè)置的,而對(duì)每個(gè)HeadView下面所屬的subItem設(shè)置left、top、right、bottom就比較難了,因?yàn)槿绻凑铡禦ecyclerView GridLayoutManager item設(shè)置萬(wàn)能分隔線》中直接用絕對(duì)position進(jìn)行計(jì)算的話,界面就亂了。
下面上一個(gè)圖:

在上圖中
區(qū)域一/區(qū)域二:代表了一個(gè)區(qū)塊HeadView+subItems,而我在subItem上都標(biāo)了0、1、2、3...等。這些標(biāo)記的數(shù)字就代表相對(duì)position,而他們本身絕對(duì)position這是他們本身實(shí)際的position:可能是1、2、3、5、6、7、8這樣。
而區(qū)域二中圖標(biāo)0的Item實(shí)際position是多少呢? 是5! 那我們?nèi)绾蔚玫狡湎鄬?duì)position為0?那我們就需要用絕對(duì)position 減去 包含其headView之上的Item數(shù)量。相當(dāng)于5-5=0。 然后其后面Item的相對(duì)position 也是6-5=1、7-5=2、8-5=3(圖上標(biāo)4的給標(biāo)錯(cuò)了??)
那這樣看就只有相對(duì)position才能套用Ln = (position % spanCount) * (dW-eW) + sW這個(gè)公式了。
劃重點(diǎn):subItem的相對(duì)position就是相對(duì)于包括他的HeadView Item以及上面 所有的Item。
3.3 我們?cè)趺传@取subItem的相對(duì)position
通過(guò)3.1,我們能區(qū)分HeadView和subItem了。這點(diǎn)很重要
3.3.1 首先我們先定義兩個(gè)HashMap用于存儲(chǔ)position信息
/**
* 意思是存儲(chǔ)每一個(gè)個(gè)HeadView 的之前所有Item包括自己的數(shù)量
*/
LinkedHashMap<Integer,Integer> headPositionTotalCountMap = new LinkedHashMap<>();
/**
* 每一個(gè)子Item(非HeadView),存儲(chǔ)自己對(duì)應(yīng)的headView的Item數(shù)量,
* 主要用于取余計(jì)算時(shí),位置換算
*/
LinkedHashMap<Integer,Integer> subItemPositionCountMap = new LinkedHashMap<>();
1、headPositionTotalCountMap:用于存儲(chǔ)在它之前的并包括自己的item數(shù)量
2、subItemPositionCountMap: 對(duì)應(yīng)于每個(gè)subItem存儲(chǔ)用于計(jì)算自己相對(duì)position的Items數(shù)量(每個(gè)HeadView下的subItem,相對(duì)Items數(shù)量是相等的)
3.3.2 存儲(chǔ)用于計(jì)算相對(duì)position的items total count
if(spanSize == spanCount){//這是有HeaderView 的情況
...
//如果HeadView Item沒有保存count信息,則將它之前包括自身的count,記錄
//到以其絕對(duì)position為Key的Map中
if(!headPositionTotalCountMap.containsKey(itemPosition)) {
headPositionTotalCountMap.put(itemPosition,itemPosition+1);
}
}else {//類似HeaderView 下的子item
//如果subItem沒有保存count信息,則將它HeadView記錄的Count取出,記錄
//到以其絕對(duì)position為Key的Map中
if(!subItemPositionCountMap.containsKey(itemPosition)) {
//找到headPostionTotalCountMap中最近的entry,獲取其value
int headViewTotalCount = headPositionTotalCountMap.size() == 0 ? 0 : getMapTail(headPositionTotalCountMap).getValue();
subItemPositionCountMap.put(itemPosition, headViewTotalCount);
}
...
//通過(guò)item自身記錄的相對(duì)items count,計(jì)算出相對(duì)position“(itemPosition-subItemPositionCountMap.get(itemPosition))”,套入公式
left = (itemPosition-subItemPositionCountMap.get(itemPosition)) % spanCount * (dividerItemWidth - eachItemWidth) + spaceWidth;
right = eachItemWidth - left;
bottom = 0;
top = mDividerWidth;
}
outRect.set(left, top, right, bottom);
代碼中注釋解釋了一波,這里大家就看看,理解一下~
3.4 畫出每個(gè)區(qū)域之間的虛線

//繪制不同HeadView之間虛線分割線
private void draw(Canvas canvas, RecyclerView parent) {
int width = mContext.getResources().getDisplayMetrics().widthPixels > mContext.getResources().getDisplayMetrics().heightPixels
? mContext.getResources().getDisplayMetrics().heightPixels : mContext.getResources().getDisplayMetrics().widthPixels;
int spanCount = getSpanCount(parent);
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
int itemPosition = ((RecyclerView.LayoutParams) child.getLayoutParams()).getViewLayoutPosition();
int spanSize = getSpanSize(itemPosition,parent);
if(spanCount == spanSize && itemPosition != 0){
mPath.reset();
mPath.moveTo(child.getLeft()-5,child.getTop()-mDividerWidth);
mPath.lineTo(width-mDividerWidth+5,child.getTop()-mDividerWidth);
canvas.drawPath(mPath,mPaint);
}
}
}
畫虛線就比較簡(jiǎn)單了幾大件:Path、Paint、Canvas配置好就行,然后獲取每個(gè)headView的top、left、屏幕寬度就可以畫出虛線,這里就不多說(shuō)了。
好了,到這里《不嵌套R(shí)ecyclerView,實(shí)現(xiàn)有HeaderView的GridLayoutManager》就講完了 ,它沒有嵌套R(shí)ecyclerView來(lái)實(shí)現(xiàn)文中UI,并且能很好的平分間隔,不會(huì)使item位置錯(cuò)亂。另外,可能文中一些東西沒講清楚,如若有任何問題,都可以留言,我看到后會(huì)第一時(shí)間回復(fù)!See you next article~
我已將GridDividerItemDecoration,上傳到GitHub:https://github.com/haozi5460/GridDividerMoreTypeItemDecoration
申明:禁用于商業(yè)用途,如若轉(zhuǎn)載,請(qǐng)附帶原文鏈接。http://www.itdecent.cn/p/ea4d9843dada 蟹蟹(#.#)