在Android中,消息的傳遞控制主要是通過兩個方法共同配合使用來對用戶的觸摸消息進行分發的,下面就來看看這兩個方法;
下面來看這兩個方法的定義原型;
public boolean onInterceptTouchEvent(MotionEvent ev); public boolean onTouchEvent(MotionEvent event);
這兩個方法的一個共同點就是都有一個參數MotionEvent用於獲取觸摸事件的具體信息(比如按下,移動等等消息形態)和函數返回值(用於控制不同場合的觸摸處理);
簡單說一下MotionEvent,它包含了用戶觸摸消息的類型,常用的幾種觸摸消息類型為:ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL, 分別表示觸摸按下,移動,結束的狀態;這幾種消息類型不是並發產生的,而是如同觸摸發生的次序一樣有序產生的,DOWN->MOVE->UP/CANCEL;
下邊通過一個例子來研究,分別自定義一個簡單的ViewGroup和View,用於跟蹤onTouchEvent和onInterceptTouchEvent的執行;
LLinearLayout
public class LLinearLayout extends LinearLayout { public LLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public LLinearLayout(Context context) { super(context); } private float lastY; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { String tag = "onInterceptTouchEvent"; Log.w(tag, "" + super.onInterceptTouchEvent(ev)); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.w(tag, "ACTION_DOWN"); lastY = ev.getX(); break; case MotionEvent.ACTION_MOVE: Log.w(tag, "ACTION_MOVE"); if (ev.getX() - lastY > 20) { Log.w(tag, "ACTION_MOVE>20"); return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: Log.w(tag, "ACTION_UP"); break; } return false; } @Override public boolean onTouchEvent(MotionEvent event) { Log.w("onTouchEvent", "" + super.onTouchEvent(event)); return false; } }
LView
public class LView extends ImageView { public LView(Context context, AttributeSet attrs) { super(context, attrs); } public LView(Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { String tag = "LView.onTouchEvent"; Log.e(tag, "" + super.onTouchEvent(event)); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(tag, "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(tag, "ACTION_MOVE"); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: Log.e(tag, "ACTION_UP"); break; } return false; } }
在xml文件中將LView加入LLinearLayout中,通過一系列更改來一控究竟;
step1:
通過上面的Log信息,可以看出,觸摸事件先被LLinearLayout攔截到(onInterceptTouchEvent),在這個Demo中,該方法返回值為false,接著就將Touch事件傳遞給LView,LView的onTouchEvent響應了此次觸摸事件,並且也返回false;然後Touch事件再傳遞給LLinearLayout的onTouchEvent進行處理;
從上面我們可以簡單的看出,Touch事件先被LLinearLayout攔截到,然後傳遞給LView,LView執行onTouchEvent處理邏輯;然後LLinearLayout再執行自己的onTouchEvent處理邏輯;@1
step2:將上面LLinearLayout的onInterceptTouchEvent方法返回值改為true,再次運行程序;
相比上面的Log信息,可以看到消息沒有傳遞到LView;到這裡,應該可以得出一個小小的結論了吧;
ViewGroup裡面的onInterceptTouchEvent返回值,返回true表示攔截Touch事件,不再將Touch事件傳遞給ViewGroup裡面的子View;
step3: 回到step1,將LView中onTouchEvent返回值改為true,再次運行程序,手指從屏幕右滑到左;
從Log信息中,可以看出,LView的onTouchEvent返回值為true時,LView的觸摸事件從DOWN傳遞到了MOVE,再傳遞到UP;當然,整個過程都是先由LLinearLayout的onInterceptTouchEvent先接收到Touch事件,在這裡,並沒有攔截Touch事件,而是將Touch事件傳遞給子View;細心的朋友可能會發現,在這裡,並沒有執行到LLinearLayout的onTouchEvent方法,why?其實是因為LView的onTouchEvent事件返回了true,表示處理消耗了此事件,不再繼續傳遞,也就不執行到LLinearLayout的onTouchEvent方法;
結論:View的onTouchEvent返回值表示是否將繼續傳遞Touch事件,比如如果返回true,觸摸形態將會從DOWN傳遞到MOVE再到UP(這裡這個說法其實不嚴謹,這裡指的是整個onTouchEvent方法返回值都是true,而沒有分段返回,比如在MOVE形態時返回了false);
step4:繼續step3,運行程序,手指從屏幕左滑到右;
在LLinearLayout的onInterceptTouchEvent中,如果MOVE消息向右滑動距離大於20,則將攔截Touch事件,所以,在事件被攔截後,將不會再像step3中,MOVE消息會在LView的onTouchEvent中不斷傳遞,而是被中斷,觸摸形態變為到ACTION_UP即手指抬起(其實這裡這樣說是不對的,准確的說是觸摸形態變為ACTION_CANCEL,因為我將UP和CANCEL處理為一類,所以如上圖,打印出來的Log信息為ACTION_UP,實際上它本質是ACTION_CANCEL);然後由於ACTION_MOVE被攔截,所以在手指MOVE的時候LLinearLayout的onTouchEevent不斷被調用;
結尾總結:
ViewGroup裡的onInterceptTouchEvent默認值是false,只有當返回值是false的時候,Touch事件才傳給子View,然後調用到子View的onTouchEvent;返回值為true的時候,將攔截用戶Touch事件,子View則捕獲不到觸摸事件;
View的onTouchEvent方法,當返回值為true的時候,事件將繼續往下傳遞,由ACTION_DOWN傳遞到ACTION_MOVE再到ACTION_UP,反之如果返回值為false,則只會捕獲到MotionEvent的ACTION_DOWN形態;
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11