序:前邊我因為項目需要擼了一下RecyclerView GridLayoutManager item設置萬能分隔線弥雹,感覺還是實用的吧垃帅,閱讀量也水漲船高,令人欣喜剪勿,也滿足了內心的小九九~~~ ??
至于這篇文章呢贸诚,是在上一篇的基礎上做加法,增加了HeadView的顯示窗宦。所以至于Item間距啥的算法赦颇,原理之類的這里就不再講解了。沒看過上一篇文章的親們赴涵,直接先去擼一把~
下面先貼個圖:
看到這個圖大家第一想到的做法是什么媒怯?
讓我們猜猜看:是不是一個RecyclerView(LinearLayoutManager)嵌套另一個RecyclerView(GridLayoutManager)?
嗯!我們一般都是這么做的髓窜,也沒什么不妥扇苞。
但是這里呢,我們換個角度寄纵,換個思維鳖敷,嘗試用給一個RecyclerView給它解決了~,年輕就是要燥??
照舊~ 無圖無真相??
一程拭、首先定踱,我們要對我們需要顯示的Item進行ViewType區(qū)分
@Override
public int getItemViewType(int position) {
if(listData != null)
return listData.get(position).getViewType();
return super.getItemViewType(position);
}
大家都看得懂哈~ 略...
二、我們要對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;
}
});
這里實際上就是獲取Item的ViewType崖媚,如果是HeadView的Type就將GridLayoutManger的spanCount亦歉,變成一行。
spanSize 是說用多少個Item空間來顯示這個View(比如說可以用2個Item位置顯示該View畅哑,也可以3個等最大不超過設置的SpanCount)肴楷,我們這里是獲取spanCount,也就是3個荠呐,相當于一行赛蔫。
三、定制適合多個ViewType的ItemDecoration
其實泥张,使用一個RecyclerView來做圖1.1的效果呵恢,最重要的是要定制適合的ItemDecoration,前面文章詳解了GridLayout Item之間平均間距的原理和實現方法圾结。這里就不多說了瑰剃。
為了實現HeadView和子Item同在一個RecyclerView,并且能正確的設置他們之間的間距筝野,看起來還是挺繁瑣的,我們來試試看
3.1 首先我們要在自定義的ItemDecoration中粤剧,區(qū)分HeadView和subItem
int spanCount = getSpanCount(parent);
int spanSize = getSpanSize(itemPosition,parent);
if(spanSize == spanCount){//這是有HeaderView 的情況
...
}else {//類似HeaderView 下的子item
...
}
這里我們可以通過獲取GridLayoutManager的SpanCount和item的SpanSize歇竟,如果這兩者相等就說明是HeadView Item,因為我們之前在設置GridLayoutManager spansize的時候設置為gridLayoutManager.getSpanCount()
抵恋。
3.2 通過上一步我們能夠區(qū)分HeadView和subItem焕议,但是仍然有個問題是:我們如何能知道每個Item的相對Position?
什么意思呢弧关?
1盅安、比如說我們Item的絕對position,是按照順序排列的世囊,0别瞭、1、2株憾、3蝙寨、4....等等
(實際上就是上一篇《RecyclerView GridLayoutManager item設置萬能分隔線》中使用到的position)
2、而我所說的Item的相對position嗤瞎,有點難解釋
先貼個上個文章的公式
第一個Item:L0=sW R0=eW-sW
第二個Item:L1=dW-R0=dW-eW+sW R1=eW-L1=2eW-dW-sW
第三個Item:L2=dW-R1=2(dW-eW)+sW R2=eW-L2=3eW-2dW-sW
所以根據以上可以得出
Ln = (position % spanCount) * (dW-eW) + sW
Rn = eW-Ln
這里可看出我們要獲取Ln = (position % spanCount) * (dW-eW) + sW
中的position墙歪,而這里的這個position就不是上篇文章那個絕對Position了。
對于HeadView的left贝奇、top虹菲、right、bottom都是要設置的掉瞳,而對每個HeadView下面所屬的subItem設置left毕源、top髓帽、right、bottom就比較難了脑豹,因為如果按照《RecyclerView GridLayoutManager item設置萬能分隔線》中直接用絕對position進行計算的話郑藏,界面就亂了。
下面上一個圖:
在上圖中
區(qū)域一/區(qū)域二:代表了一個區(qū)塊HeadView+subItems瘩欺,而我在subItem上都標了0必盖、1、2俱饿、3...等歌粥。這些標記的數字就代表相對position,而他們本身絕對position這是他們本身實際的position:可能是1拍埠、2失驶、3、5枣购、6嬉探、7、8這樣棉圈。
而區(qū)域二中圖標0的Item實際position是多少呢涩堤? 是5! 那我們如何得到其相對position為0分瘾?那我們就需要用絕對position 減去 包含其headView之上的Item數量胎围。相當于5-5=0。 然后其后面Item的相對position 也是6-5=1德召、7-5=2白魂、8-5=3(圖上標4的給標錯了??)
那這樣看就只有相對position才能套用Ln = (position % spanCount) * (dW-eW) + sW
這個公式了。
劃重點:subItem的相對position就是相對于包括他的HeadView Item以及上面 所有的Item上岗。
3.3 我們怎么獲取subItem的相對position
通過3.1福荸,我們能區(qū)分HeadView和subItem了。這點很重要
3.3.1 首先我們先定義兩個HashMap用于存儲position信息
/**
* 意思是存儲每一個個HeadView 的之前所有Item包括自己的數量
*/
LinkedHashMap<Integer,Integer> headPositionTotalCountMap = new LinkedHashMap<>();
/**
* 每一個子Item(非HeadView),存儲自己對應的headView的Item數量液茎,
* 主要用于取余計算時逞姿,位置換算
*/
LinkedHashMap<Integer,Integer> subItemPositionCountMap = new LinkedHashMap<>();
1、headPositionTotalCountMap:用于存儲在它之前的并包括自己的item數量
2捆等、subItemPositionCountMap: 對應于每個subItem存儲用于計算自己相對position的Items數量(每個HeadView下的subItem滞造,相對Items數量是相等的)
3.3.2 存儲用于計算相對position的items total count
if(spanSize == spanCount){//這是有HeaderView 的情況
...
//如果HeadView Item沒有保存count信息,則將它之前包括自身的count栋烤,記錄
//到以其絕對position為Key的Map中
if(!headPositionTotalCountMap.containsKey(itemPosition)) {
headPositionTotalCountMap.put(itemPosition,itemPosition+1);
}
}else {//類似HeaderView 下的子item
//如果subItem沒有保存count信息谒养,則將它HeadView記錄的Count取出,記錄
//到以其絕對position為Key的Map中
if(!subItemPositionCountMap.containsKey(itemPosition)) {
//找到headPostionTotalCountMap中最近的entry,獲取其value
int headViewTotalCount = headPositionTotalCountMap.size() == 0 ? 0 : getMapTail(headPositionTotalCountMap).getValue();
subItemPositionCountMap.put(itemPosition, headViewTotalCount);
}
...
//通過item自身記錄的相對items count买窟,計算出相對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 畫出每個區(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);
}
}
}
畫虛線就比較簡單了幾大件:Path瞳购、Paint、Canvas配置好就行亏推,然后獲取每個headView的top学赛、left、屏幕寬度就可以畫出虛線吞杭,這里就不多說了盏浇。
好了,到這里《不嵌套RecyclerView芽狗,實現有HeaderView的GridLayoutManager》就講完了 绢掰,它沒有嵌套RecyclerView來實現文中UI,并且能很好的平分間隔童擎,不會使item位置錯亂滴劲。另外,可能文中一些東西沒講清楚柔昼,如若有任何問題哑芹,都可以留言,我看到后會第一時間回復捕透!See you next article~
我已將GridDividerItemDecoration,上傳到GitHub:https://github.com/haozi5460/GridDividerMoreTypeItemDecoration
申明:禁用于商業(yè)用途碴萧,如若轉載乙嘀,請附帶原文鏈接。http://www.reibang.com/p/ea4d9843dada 蟹蟹(#.#)