使用ItemDecoration打造懸停分組頭部,網(wǎng)上很多,但是大部分頭部都是一些文字圖形啥的,不夠靈活,于是我在此基礎(chǔ)上使用了自定義view來顯示分組頭部和懸停view。
先上圖:
還有footer,視屏有限制,就不放上來了。
可以看到頁面上是一個(gè)recyclerView。可以分為以下這幾部分,recyclerview,header, group(分組頭部),以及懸停頭部。懸停分組的知識(shí)已經(jīng)很多人寫了,我就不贅述了。 我主要說說這些自定義view是怎么畫上去的。
來看代碼:
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (header != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(child);
if (position == 0) {
c.translate(0, child.getTop() - header.getMeasuredHeight() - params.topMargin);
header.draw(c);
c.translate(0, header.getMeasuredHeight() - child.getTop() + params.topMargin);
}
}
}
}
這是給recyclerview加頭部的代碼。其中header需要我們提前給他處理好計(jì)算代碼放在最后,這其中的關(guān)鍵代碼即 c.translate(0, child.getTop() - header.getMeasuredHeight() - params.topMargin);
設(shè)置繪制header的起點(diǎn)。繪制完后,把canvas還原,如不還原會(huì)導(dǎo)致recycler view本體繪制出問題(ondraw在recycler view之前繪制)。
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (group == null) {
return;
}
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(child);
if (isGroupFirst(position)) {
((BaseGroupRecyclerView) parent).setGroupData(group, position);
buildView(group);
int top = child.getBottom() - child.getHeight() - group.getMeasuredHeight() - params.topMargin;
c.translate(0, top);
group.draw(c);
c.translate(0, -top);
}
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
if (!isStickHeader) {
return;
}
final int childCount = parent.getChildCount();
int preGroupName;
int currentGroupName = -1;
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
preGroupName = currentGroupName;
currentGroupName = mList.get(position);
if (currentGroupName == preGroupName) {
continue;
}
int viewBottom = view.getBottom();
int top = Math.max(group.getMeasuredHeight(), view.getTop());
if (position + 1 < state.getItemCount()) {
int nextGroupName = mList.get(position + 1);
if (currentGroupName != nextGroupName && viewBottom + params.topMargin * 2 < top) {
top = viewBottom + params.topMargin * 2;
}
}
((BaseGroupRecyclerView) parent).setGroupData(group, position);
buildView(group);
int marginTop = top - group.getMeasuredHeight() - params.topMargin;
c.translate(0, marginTop);
group.draw(c);
c.translate(0, -marginTop);
break;
}
}
這段代碼就是繪制分組頭部(onDraw方法),在最上面懸停的頭部(onDrawOver方法)。
我們來讀一下這段代碼。先多group判空, 然后對(duì)屏幕上顯示的item循環(huán),看那個(gè)是這一分組的第一個(gè),isGroupFirst這個(gè)方法自己定義呀,
((BaseGroupRecyclerView) parent).setGroupData(group, position);
buildView(group);
這兩句是我用來根據(jù)位置信息改變group顯示的內(nèi)容,可以忽略。我是在自定義recycler view里面生成此用于分組的itemdecoration對(duì)象,所以我的recycler view里可以直接操作此group對(duì)象。
buildView這個(gè)是因?yàn)楦牧薵roup的內(nèi)容,重新測量一下group的寬高。
接下來 計(jì)算group的起點(diǎn),不熟悉的可以自己查查手機(jī)屏幕坐標(biāo)系。
然后group.draw,把group畫出來。再還原c的起點(diǎn)。
onDrawOver里的思想是當(dāng)某一個(gè)分組頭部到屏幕頂了, 就在recycler view上邊畫出此group,制造出懸浮的效果。具體邏輯就不看了。主要的關(guān)鍵就在canvas的位置的轉(zhuǎn)換。
寫在最后,一開始我是看了這篇文章做的自定義view:
http://blog.csdn.net/xyq046463/article/details/53009635?locationNum=3&fps=1
他就做了一個(gè)頭部,并且滑動(dòng)的時(shí)候,把頭部向上變換,我當(dāng)時(shí)并不知道可以通過c.translate來改變畫筆的起點(diǎn)位置來改變view顯示的位置,于是我就把view.buildDrawingCache()變成一張圖片,通過 :
dst.set(left, top, right, bottom);設(shè)置圖片的位置,
c.drawBitmap(bitmap, null, dst, null);畫圖片。
來調(diào)整view的位置,這種方法我leader說效率不好,view轉(zhuǎn)換成bitmap耗時(shí)。于是我繼續(xù)想辦法,突然想到那個(gè)只做頭部的老哥通過c.translate的方法來改變畫筆的位置,于是我就想到了通過這種方法來改變view的顯示方式。