Android自定義ViewGroup(二)-帶懸停標(biāo)題的ExpandableListView_第1頁(yè)
Android自定義ViewGroup(二)-帶懸停標(biāo)題的ExpandableListView_第2頁(yè)
Android自定義ViewGroup(二)-帶懸停標(biāo)題的ExpandableListView_第3頁(yè)
Android自定義ViewGroup(二)-帶懸停標(biāo)題的ExpandableListView_第4頁(yè)
Android自定義ViewGroup(二)-帶懸停標(biāo)題的ExpandableListView_第5頁(yè)
已閱讀5頁(yè),還剩3頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Android自定義ViewGroup(二)——帶懸停標(biāo)題的ExpandableListView也就是說(shuō),在某一個(gè)分組內(nèi)部滾動(dòng)時(shí),要求分組標(biāo)題懸停,當(dāng)滾出該分組范圍時(shí),把標(biāo)題頂出去,懸停下一個(gè)分組的標(biāo)題。正好看到一個(gè)比較有趣的思路,做了一個(gè)實(shí)現(xiàn),在這里分享一下。代碼結(jié)構(gòu)如下,基本上是一個(gè)MVC的架構(gòu):既然是點(diǎn)擊可收縮展開(kāi)的列表,顯然要用ExpandableListView,關(guān)于這個(gè)類(lèi)的用法這里就不贅述了,網(wǎng)上一搜一大把,其實(shí)跟ListView的用法差不多,不過(guò)它幫你分了組,所以原來(lái)Adapter里的getView()就變成了getGroupView()和getChildView(),getCount()就變成了getGroupCount()等等。另外既然要支持收縮展開(kāi),必然會(huì)提供collapseGroup()和expandGroup()等接口。下面分析如何添加懸停標(biāo)題,其實(shí)精華部分就一句話(huà):懸停標(biāo)題是畫(huà)上去的,而不是加到viewhierarchy里去,具體根據(jù)滾動(dòng)的情況確定如何畫(huà)。首先我們來(lái)寫(xiě)一個(gè)DockingExapandableListView類(lèi),繼承自ExpandableListView,包含一個(gè)View類(lèi)型的成員變量mDockingHeader。一、重寫(xiě)onMeasure()和onLayout()方法[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){super.onMeasure(widthMeasureSpec,heightMeasureSpec);if(mDockingHeader!=null){measureChild(mDockingHeader,widthMeasureSpec,heightMeasureSpec);mDockingHeaderWidth=mDockingHeader.getMeasuredWidth();mDockingHeaderHeight=mDockingHeader.getMeasuredHeight();}}@OverrideprotectedvoidonLayout(booleanchanged,intl,intt,intr,intb){super.onLayout(changed,l,t,r,b);if(mDockingHeader!=null){mDockingHeader.layout(0,0,mDockingHeaderWidth,mDockingHeaderHeight);}}這個(gè)比較簡(jiǎn)單,就是測(cè)量一下這個(gè)標(biāo)題視圖的寬度和高度。二、重寫(xiě)dispatchDraw()方法上面提到,懸停標(biāo)題是畫(huà)上去的,而不是加到viewhierarchy里去的。因此,需要在完成其他子view的繪制之后,再把懸停標(biāo)題欄畫(huà)上去:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片@OverrideprotectedvoiddispatchDraw(Canvascanvas){super.dispatchDraw(canvas);if(mDockingHeaderVisible){//drawheaderviewinsteadofaddingintoviewhierarchydrawChild(canvas,mDockingHeader,getDrawingTime());}}三、根據(jù)滾動(dòng)狀態(tài)決定如何繪制懸停標(biāo)題滾動(dòng)到不同位置,懸停標(biāo)題的顯示是不同的,因此需要根據(jù)滾動(dòng)狀態(tài)定義一個(gè)狀態(tài)機(jī)的切換。讓DockingExpandableListView實(shí)現(xiàn)OnScrollListener接口,并重寫(xiě)onScroll()方法:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片@OverridepublicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){longpackedPosition=getExpandableListPosition(firstVisibleItem);intgroupPosition=getPackedPositionGroup(packedPosition);intchildPosition=getPackedPositionChild(packedPosition);//updateheaderviewbasedonfirstvisibleitem//IMPORTANT:refertogetPackedPositionChild()://Ifthisgroupdoesnotcontainachild,returns-1.Needtohandlethiscaseincontroller.updateDockingHeader(groupPosition,childPosition);}這里有幾個(gè)比較有意思的方法,都是ExpandableListView自帶的API:getExpandableListPosition():這個(gè)API獲得一個(gè)所謂的packedposition,是一個(gè)64位的值,高32位表示group的ID,低32位表示在這個(gè)group內(nèi)部的childID。getPackedPositionGroup():獲取groupID,也就是高32位getPackedPositionChild():獲取childID,也就是低32位注意我們給getExpandableListPosition()傳的參數(shù)是firstVisibleItem,因此我們就得到了最上方的第一個(gè)可見(jiàn)項(xiàng)所屬的group以及組內(nèi)位置。接下來(lái)就是最為關(guān)鍵的updateDockingHeader()方法,根據(jù)狀態(tài)機(jī)來(lái)確定如何繪制懸停標(biāo)題。在看這個(gè)方法之前,我們先看一下有哪幾種狀態(tài),定義在IDockingController里:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片publicinterfaceIDockingController{intDOCKING_HEADER_HIDDEN=1;intDOCKING_HEADER_DOCKING=2;intDOCKING_HEADER_DOCKED=3;intgetDockingState(intfirstVisibleGroup,intfirstVisibleChild);}一共3種狀態(tài),這些狀態(tài)都是什么含義呢?參見(jiàn)下圖:DOCKING_HEADER_HIDDEN:當(dāng)分組沒(méi)有展開(kāi),或者組里沒(méi)有子項(xiàng)的時(shí)候,是不需要繪制懸停標(biāo)題的DOCKING_HEADER_DOCKING:當(dāng)滾動(dòng)到上一個(gè)分組的最后一個(gè)子項(xiàng)時(shí),需要把舊的標(biāo)題“推”出去,“??俊毙碌臉?biāo)題,所以這個(gè)狀態(tài)命名為“docking”DOCKING_HEADER_DOCKED:新標(biāo)題“??俊蓖戤?,在該分組內(nèi)部滾動(dòng),稱(chēng)為“docked”狀態(tài)基于這個(gè)狀態(tài)機(jī),我們來(lái)看一下updateDockingHeader()方法的實(shí)現(xiàn):[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片privatevoidupdateDockingHeader(intgroupPosition,intchildPosition){if(getExpandableListAdapter()==null){return;}if(getExpandableListAdapter()instanceofIDockingController){IDockingControllerdockingController=(IDockingController)getExpandableListAdapter();mDockingHeaderState=dockingController.getDockingState(groupPosition,childPosition);switch(mDockingHeaderState){caseIDockingController.DOCKING_HEADER_HIDDEN:mDockingHeaderVisible=false;break;caseIDockingController.DOCKING_HEADER_DOCKED:if(mListener!=null){mListener.onUpdate(mDockingHeader,groupPosition,isGroupExpanded(groupPosition));}//Headerviewmightbe"GONE"statusatthebeginning,sowemightnotbeable//togetitswidthandheightduringinitialmeasureprocedure.//Domanualmeasureandlayoutoperationshere.mDockingHeader.measure(MeasureSpec.makeMeasureSpec(mDockingHeaderWidth,MeasureSpec.AT_MOST),MeasureSpec.makeMeasureSpec(mDockingHeaderHeight,MeasureSpec.AT_MOST));mDockingHeader.layout(0,0,mDockingHeaderWidth,mDockingHeaderHeight);mDockingHeaderVisible=true;break;caseIDockingController.DOCKING_HEADER_DOCKING:if(mListener!=null){mListener.onUpdate(mDockingHeader,groupPosition,isGroupExpanded(groupPosition));}ViewfirstVisibleView=getChildAt(0);intyOffset;if(firstVisibleView.getBottom()<mDockingHeaderHeight){yOffset=firstVisibleView.getBottom()-mDockingHeaderHeight;}else{yOffset=0;}//TheyOffsetisalwaysnon-positive.Whenanewheaderviewis"docking",//previousheaderviewneedtobe"scrolledover".Thusweneedtodrawthe//oldheaderviewbasedonlastchild'sscrollamount.mDockingHeader.measure(MeasureSpec.makeMeasureSpec(mDockingHeaderWidth,MeasureSpec.AT_MOST),MeasureSpec.makeMeasureSpec(mDockingHeaderHeight,MeasureSpec.AT_MOST));mDockingHeader.layout(0,yOffset,mDockingHeaderWidth,mDockingHeaderHeight+yOffset);mDockingHeaderVisible=true;break;}}}其中,是否顯示懸停標(biāo)題是通過(guò)一個(gè)叫做mDockingHeaderVisible的boolean變量控制的,這個(gè)在上面的dispatchDraw()方法里也見(jiàn)到了。重點(diǎn)看“docking”狀態(tài)的處理:通過(guò)計(jì)算第一個(gè)可見(jiàn)項(xiàng)的bottom和高度之間的差異,也就是這個(gè)yOffset,確定懸停標(biāo)題在y軸方向的偏移量。這樣在繪制懸停標(biāo)題的時(shí)候,我們就只能看到一部分,造成一種被“推出去”的感覺(jué)。四、懸停標(biāo)題狀態(tài)機(jī)在剛剛提到的那個(gè)IDockingController接口里有一個(gè)方法叫g(shù)etDockingState(),在updateDockingHeader()方法里就是通過(guò)調(diào)用這個(gè)方法來(lái)確定當(dāng)前懸停標(biāo)題的狀態(tài)的。DockingExpandableListViewAdapter實(shí)現(xiàn)了該接口和方法,完成狀態(tài)機(jī)狀態(tài)轉(zhuǎn)換:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片@OverridepublicintgetDockingState(intfirstVisibleGroup,intfirstVisibleChild){//Noneedtodrawheaderviewifthisgroupdoesnotcontainanychild&alsonotexpanded.if(firstVisibleChild==-1&&!mListView.isGroupExpanded(firstVisibleGroup)){returnDOCKING_HEADER_HIDDEN;}//Reachingcurrentgroup'slastchild,preparingfordockingnextgroupheader.if(firstVisibleChild==getChildrenCount(firstVisibleGroup)-1){returnIDockingController.DOCKING_HEADER_DOCKING;}//Scrollinginsidecurrentgroup,headerviewisdocked.returnIDockingController.DOCKING_HEADER_DOCKED;}邏輯非常簡(jiǎn)單清晰:如果當(dāng)前group沒(méi)有子項(xiàng),并且也不是展開(kāi)狀態(tài),就返回DOCKING_HEADER_HIDDEN狀態(tài),不繪制懸停標(biāo)題;如果到達(dá)了當(dāng)前group的最后一個(gè)子項(xiàng),進(jìn)入DOCKING_HEADER_DOCKING狀態(tài);其他情況,在當(dāng)前group內(nèi)部滾動(dòng),返回DOCKING_HEADER_DOCKED狀態(tài)。五、Touch事件處理文章最前面提到過(guò),這個(gè)標(biāo)題視圖是畫(huà)上去,而不是添加到viewhierarchy里的,因此它是無(wú)法響應(yīng)touch事件的!那就需要我們自己根據(jù)點(diǎn)擊區(qū)域進(jìn)行判斷了,需要重寫(xiě)onInterceptTouchEvent()和onTouchEvent()方法:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片@OverridepublicbooleanonInterceptTouchEvent(MotionEventev){if(ev.getAction()==MotionEvent.ACTION_DOWN&&mDockingHeaderVisible){Rectrect=newRect();mDockingHeader.getDrawingRect(rect);if(rect.contains((int)ev.getX(),(int)ev.getY())&&mDockingHeaderState==IDockingController.DOCKING_HEADER_DOCKED){//Hitheaderviewarea,interceptthetoucheventreturntrue;}}returnsuper.onInterceptTouchEvent(ev);}//Note:Asheaderviewisdrawntothecanvasinsteadofaddingintoviewhierarchy,//it'suselesstosetitstouchorclickeventlistener.Needtohandletheseinput//eventscarefullybyourselves.@OverridepublicbooleanonTouchEvent(MotionEventev){if(mDockingHeaderVisible){Rectrect=newRect();mDockingHeader.getDrawingRect(rect);switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:if(rect.contains((int)ev.getX(),(int)ev.getY())){//forbideventhandlingbylistview'sitemreturntrue;}break;caseMotionEvent.ACTION_UP:longflatPostion=getExpandableListPosition(getFirstVisiblePosition());intgroupPos=ExpandableListView.getPackedPositionGroup(flatPostion);if(rect.contains((int)ev.getX(),(int)ev.getY())&&mDHeaderState==IDockingController.DOCKING_HEADER_DOCKED){//handleheaderviewclickevent(dogroupexpansion&collapse)if(isGroupExpanded(groupPos)){collapseGroup(groupPos);}else{expandGroup(groupPos);}returntrue;}break;}}returnsuper.onTouchEvent(ev);}這部分實(shí)現(xiàn)比較簡(jiǎn)單易懂,如果當(dāng)前是DOCKING_HEADER_DOCKED狀態(tài),并且點(diǎn)擊區(qū)域命中了標(biāo)題視圖的drawingrect,那么就需要攔截to

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論