標籤:android style blog http color 使用 io strong ar
在Android中,訊息的傳遞控制主要是通過兩個方法共同配合使用來對使用者的觸摸訊息進行分發的,下面就來看看這兩個方法;
- onInterceptTouchEvent:此方法定義於ViewGroup中,顧名思義,這個方法是用於ViewGroup攔截(intercept)觸摸訊息的;
- onTouchEvent:此方法定義於View中,用於處理使用者的觸摸事件;
下面來看這兩個方法的定義原型;
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 Touch訊息傳遞機制探究分析