側(cè)滑面板屬于繼承于ViewGroup的自定義控件。
注意:
1.繼承ViewGroup的自定義View,其中的onMesure()和onLayout()方法是成對出現(xiàn)的,如果onLayout()方法被調(diào)用,那么onMesure()方法肯定在之前被調(diào)用。
2.關(guān)于onTouchEvent的返回值。
如果是繼承View或者是ViewGrou,return true;
如果是繼承已有控件擴(kuò)展其功能,比如說RefreshListView,return supre 不會屏蔽掉原有的功能。
3.滾動:手指在屏幕上往下滾動,內(nèi)容是往上滾。手指在屏幕上往上滾,內(nèi)容是向下滾動。手指在屏幕上向左滾動,內(nèi)容是向右滾動(即逐漸顯示的是右邊的內(nèi)容)。手指在屏幕上向右滾動,內(nèi)容是向左滾動(即逐漸顯示的是左側(cè)的內(nèi)容)
如:scrollTo(10,0):手指向右滾動10個像素,屏幕里的內(nèi)容應(yīng)該是向左滾動10個像素,因此可以想象成,內(nèi)容位置不變,屏幕向右滾動10個像素。

4.scrollTo和scrollBy的區(qū)別:scrollTo是滾動到。滾動到10像素,-30像素
scrollBy,在原來的基礎(chǔ)上滾動,假如上一次滾動到10像素,如果此時使用scrollBy(30,0)就是向右滾動到40像素處;而假如此時使用scrollBy(-20,0)就是滾動到-10像素的位置,即向左滾動到-10像素處
1.自定義SlidingMenu繼承ViewGroup,在布局文件中往slidingMenu中擺放左側(cè)菜單和主面板。

2.在slidingMenu中測量和擺放左面板和主面板。
1)測量:在ViewGroup中獲取孩子控件的方法是:getChildAt(index);index是按照子控件加入的順序來遞增的。
其中l(wèi)eftMenu.measure(width,heightMeasureSpec);寬度是左面板控件的寬度,高度是match_parent。傳入?yún)?shù)widthMeasureSpec或者h(yuǎn)eightMeasureSpec,就是指寬度或者高度為match_parent.

2).擺放? onLayout()五個參數(shù)的意思如下圖所示。我們可以打印出 l t r b的值,可以看到,打印出的值就是屏幕邊界值。當(dāng)擺放子控件時,layout()方法中的四個參數(shù)分別是:需要把子控件擺放到的位置的左邊距,上邊距,下邊距,右邊距。注意,此時使用getMeasureWidth獲取左面板的寬度可以獲取到值,因為leftMenu已經(jīng)調(diào)用過measure方法。


3.如上面第三點(diǎn)分析的:滾動,當(dāng)要全部露出左面板時,是手指向右滾動,左側(cè)內(nèi)容逐漸顯示,即內(nèi)容向左滾動??梢钥闯善聊幌蛴覞L動了240個像素。方法應(yīng)該是scrollTo(-240,0):(可以簡單的理解成滾動屏幕,向左將屏幕滾動了240;屏幕向左滾動240也可以直接寫出方法scrollTo(-240,0))
當(dāng)手指在屏幕上滾動時:(moveX-downX)是手指在屏幕上移動的距離,而屏幕的滾動和手指移動的方向相反。因此scrollTo
或者scrollBy的參數(shù)應(yīng)該是 -(moveX - downX)
注意:只需要記住,滾動時,手指的方向和內(nèi)容的方向相反。可以理解成屏幕可動,手指的方向和屏幕的方向一致。
4.處理onTouchEvent:每次手指在屏幕上移動都需要將downX重新賦值,賦值成上一次移動的moveX,也就是將上一次moveX作為此次手指移動的起點(diǎn),不然會產(chǎn)生很大誤差,具體原因在注釋中已經(jīng)寫明。

5.手指抬起時事件的處理:手指抬起時,如果滾動到<左側(cè)面板1/2寬度時,收起左側(cè)面板。滾動到>左側(cè)面板1/2寬度時,彈出左側(cè)面板。

手指抬起時,我們也可以給側(cè)滑面板的打開和收起添加一個平滑動畫,當(dāng)快要完全打開和關(guān)閉時,來一個變慢的效果。使用Android提供的Scroller可以實現(xiàn)這個效果。使用Scroller有兩個步驟
1.開始模擬數(shù)據(jù),即獲得開始坐標(biāo),移動的距離dx,動畫持續(xù)的時間

2.在computeScroll()中不斷獲取當(dāng)前滾動到的位置的X坐標(biāo),維持動畫的持續(xù),即只要動畫未結(jié)束,就時時獲取滾動到的點(diǎn)的X坐標(biāo)currentX,調(diào)用ScrollTo方法,滾動到currentX

此處需要注意,在使用動畫時,需要時時重繪界面,重繪界面調(diào)用的是invalidate(),在繼承自View的自定義控件中,調(diào)用invalidate()使onDraw()被調(diào)用,而在繼承ViewGroup的自定義控件中,調(diào)用invalidate()使drawChild()被調(diào)用。

在drawChild()中,會調(diào)用computeScroll(),那么在重寫的computeScroll()調(diào)用invalidate()這樣就可以實現(xiàn)每次更改坐標(biāo),都可以重繪界面。
6.給back鍵添加點(diǎn)擊事件。根據(jù)狀態(tài)打開或者關(guān)閉。

在界面上調(diào)用switchState()方法即可。
7.解決手指在左面板上滑動無法收起左面板的問題,這是因為ScrollView攔截了父控件(slidingMenu)的點(diǎn)擊事件。解決方法:只需要在父類中把手指左右滑的事件給攔截掉即可。


事件的傳遞由上往下傳遞
事件分法機(jī)制涉及到三個方法:如下表所示。

一個事件,由Window-->Activity-->FrameLayout-->RelativeLayout-->Button在這個過程中,當(dāng)事件傳遞到FrameLayout時,首先調(diào)用的是FrameLayout的dispatchTouchEvent()方法,在FrameLayout中該方法的返回值如果是super,則會調(diào)用其自身的onInterceptTouchEvent()。返回true,消費(fèi)事件,調(diào)用onTouchEvent()方法處理,事件不會往下傳遞,后續(xù)事件(Move、Up)會繼續(xù)分發(fā)到該View。返回false,不消費(fèi)該事件,事件回傳給父控件的onTouchEvent()處理。如果其自身的onInterceptTouchEvent()返回1.ture,說明要攔截該事件,事件不會向下傳遞,此時會執(zhí)行自己的onTouchEvent(),并且同一個事件列的其他事件(Move、Up等)都會交由該View處理,在同一個事件列中,該方法不會被再次調(diào)用;2.false 默認(rèn)返回都是false:表示不攔截事件,事件會繼續(xù)向下傳遞,傳遞給RelativeLayout,調(diào)用RelativeLayout中的dispatchTouchEvent(),繼續(xù)同樣的過程。
如果上面的控件都不攔截該事件,事件傳遞到最下面的Button,Button繼承View,它其中不再有onInterceptTouchEvent(),如果dispatchTouchEvent()返回super,會調(diào)用自身的onTouchEvent(),到了onTouchEvent(),返回true,自己處理一系列事件。返回false,不處理事件,事件往回拋,向上傳遞給父控件的onTouchEvent()處理,如果父控件的onTouchEvent()不處理該事件,繼續(xù)往回拋,到最上層都沒有人處理該事件,則事件消失。(onTouchEvent和onInterceptTouchEvent返回值默認(rèn)為false)