接上一篇
<Android 開發實踐 ViewGroup 實現左右滑出視窗(一)http://www.cnblogs.com/inkheart0124/p/3532862.html>
源碼: http://files.cnblogs.com/inkheart0124/PushSlider.zip
ViewGroup中touch事件的處理,有三個函數
public boolean dispatchTouchEvent(MotionEvent ev);事件派發
public boolean onInterceptTouchEvent(MotionEvent ev);事件攔截
public boolean onTouchEvent(MotionEvent ev);事件處理
(廢話一句,View中只有兩個,dispatchTouchEvent和onTouchEvent,ViewGroup繼承了View以後增加了onInterceptTouchEvent)
一次完整的touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE....->ACTION_UP
其中對ACTION_DOWN的處理傳回值比較重要,如果dispatchTouchEvent時返回false,那表示這一次touch事件我都不需要,之後的move和up都不會再傳給我的ViewGroup。
action ==((gdHandled) && (action == + action + + action + action ==((gdHandled) && (action == + action + + action + action =
重寫這三個函數,先把event直接丟給mGDetector處理,然後在調用系統的派發機制把訊息傳遞到子View。
注意Down訊息一定不能攔截,ViewGroup主要根據move和up的情況來區別手勢,當收到down訊息的時候,我們還不能確定使用者是要滑動呢還是要操作子view中的控制項。Down訊息如果被攔截,意味著這一個訊息組都不會再傳給子view。
onTouchEvent中什麼都沒做,直接返回了true,這個方法會在dispatchTouchEvent函數調用super.dispatchTouchEvent(ev)時跑到,它的傳回值就是super.dispatchTouchEvent(ev)的傳回值,這裡至少在down訊息的時候必須返回true,否則就像前面說的那樣,整組訊息都不會再傳遞給我的pushslider了。
手勢:
PushSlider類實現了android.view.GestureDetector.OnGestureListener介面,在前一篇的建構函式中我們建立了一個手勢對象,並註冊監聽。
mGDetector = GestureDetector(mContext,);
下面就要實現OnGestureListener中的方法
public boolean onDown(MotionEvent ev);處理down訊息
public void onLongPress(MotionEvent arg0);長按
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);拖動,收到move訊息時會跑到
public boolean onFling(MotionEvent eDown, MotionEvent eMove, float velocityX,float velocityY);快速滑拋
public void onShowPress(MotionEvent arg0);發生down訊息後,用來顯示高亮等可視化的處理
public boolean onSingleTapUp(MotionEvent ev);處理一次輕觸
詳細的解釋就不寫了,android developer官網上解釋的很詳細。這裡我只需要處理onFling和onSingleTapUp,onScroll的代碼後來沒用,屏蔽掉了。
onFling 是發生donw-move-move....-up時,最後up事件時會調用到,根據入參float velocityX和float velocityY這兩個速度值,決定這是不是我們想要的一次滑拋手勢
boolean onFling(MotionEvent eDown, MotionEvent eMove, velocityX, log( + eDown.getX() + + eMove.getX() + + ((eDown.getX() - eMove.getX() > FLING_DISTANCE*mDensity) && (Math.abs(velocityX)> flingTo(MOVE_FLAG_ALLOW_LEFT, log( ((eMove.getX() - eDown.getX() > FLING_DISTANCE*mDensity) && (Math.abs(velocityX)> flingTo(MOVE_FLAG_ALLOW_RIGHT, log( log( }
5~11行,手勢是從右向左 <<<--
14~20行,手勢是從左向右 -->>>
根據eMove和eDown的相對位移,及X方向的速度,判斷是否滑到下一頁。
onSingleTapUp 是發生down-up這樣的一次點擊(輕觸)時調用
Rect focusRect = x = left = focusRect.left- right = focusRect.right- ((x < left) && flingTo(MOVE_FLAG_ALLOW_RIGHT, log( } ((x > right) && flingTo(MOVE_FLAG_ALLOW_LEFT, log( log( }
3,4行,取得當前focus的子view的地區;
6,7行,這裡取到的HitRect是相對與viewgroup的0,0點的相對座標。需要換算成實際座標,mScroller.getCurrX()是viewgroup的x位置相對於螢幕原點的值。
由於我有一個滑出子view是半屏的,當這個半屏的設定菜單被喚出後,我希望輕觸一下菜單以外的地區 就可以關閉菜單回到主介面。重寫onSingleTapUp方法就是為了處理這種情況。
flingTo,關於實現滑動,其實有很多方法,最直觀的就像我們在onLayout中做的那樣,重新算好每個子View的位置,然後layout一遍。
但我覺得用view自己的Scroller類是最簡單最省心的。
flingTo( startX = endX= (direct == focusedIndex ++ (focusedIndex == endX = (focusedIndex == endX = dx = endX - mScroller.startScroll(startX, , dx, ,(fast?: } focusedIndex -- (focusedIndex == endX = (focusedIndex == endX = dx = endX - mScroller.startScroll(startX, , dx, ,(fast?: log( + }
5行,startx,得到當前ViewGroup的位置(相對於螢幕原點);
10~15行,切換焦點,根據新的焦點得到endX,及滑動後ViewGroup的位置;
18行,直接調用Scroller的startScroll方法,就開始滑動了,很簡單吧!
startScroll原型:public void startScroll(int startX, int startY, int dx, int dy, int duration)
5個入參意義一目瞭然。
flingTo函數的兩個入參 direct是滑動方向,還有一個boolean fast,表示滑動速度,用它來確定startScroll的duration,100和250是實測的經驗值。
當滑拋手勢喚出或關閉半屏菜單時,fast=false。讓使用者看到一個清晰的滑動過程。
當使用者按menu或cancel鍵的時候,fast=true。快速喚出或關閉介面。
menu鍵和canel鍵的處理,我沒有把它們定義在PushSlider的管理範疇內。這兩個索引值仍然是有activity處理,PushSlider只需要給出一個public的方法。
簡單的說,PushSlider就管自己的3個子View,他們的顯示,位置,滑動,焦點。具體功能上的東東全都是activity的活。
因此PushSlider除了讓activity監聽焦點變化外(見上一篇),還需要提供一些查詢和操作介面,比如查詢當前焦點,查詢當前是否處於滑動中。
比較重要的操作有兩個:
public void pushForbid(boolean forbid){
mForbidden = forbid;
}
某些情況下,activity可能想不讓使用者滑動切換焦點,就可以pushForbid。
另一個public void moveToPageById(int index),activity可直達某個子View,比如前面提到的 按menu鍵喚出設定菜單的子View。
具體代碼不貼了,仍然是調用flingTo(fast)的模式。
PushSlider搞定了,看一下activity中怎麼用它。
layout/main.xml
6~15行,加入PushSlider,插入三個子View,分別是
mPushView = screenWidth = mPushView.setPageWidth(PushSlider.SLIDER_PAGE_LEFT, screenWidth/2 mPushView.setOnPageChangedListener( }
5行,把main.xml設為contect view;
7~13行,獲得PushSlider物件控點,設定左右兩個view的寬度,設定焦點切換監聽。
oh~~~ 全篇完~~
新年攢人品,稍後發源碼,還不知道怎麼上傳源碼呢