1. 程式人生 > >Android觸控事件傳遞機制學習筆記

Android觸控事件傳遞機制學習筆記

1、Android 觸控事件傳遞機制
http://blog.csdn.net/awangyunke/article/details/22047987
2、Android-onInterceptTouchEvent()和onTouchEvent()總結
http://blog.csdn.net/lvxiangan/article/details/9309927
3、Android onTouchEvent, onClick及onLongClick的呼叫機制
http://blog.csdn.net/ddna/article/details/5451722
4、Android: 詳解觸控事件如何傳遞
http://www.cnblogs.com/superlcr/p/3946034.html?utm_source=tuicool


一、涉及的類和方法
Activity,View,ViewGroup(View的子類)
1)public boolean dispatchTouchEvent(MotionEvent ev) 這個方法用來分發TouchEvent,在Activity和View裡分別定義
這個方法嘗試將觸控事件交給自己的子檢視 (如果有的話) 處理: 呼叫子檢視的 dispatchTouchEvent()
或者自己處理: 呼叫自己的 onTouchEvent() 或 OnTouchListener.onTouch()(優先呼叫)
無論是自己的子檢視,還是自己,只要完成了事件處理,都返回 true
2)public boolean onInterceptTouchEvent(MotionEvent ev) 這個方法用來攔截TouchEvent
ViewGroup單獨定義的方法,如果事件需要在該 ViewGroup 截斷 (自己處理該事件, 不再傳遞給其子檢視), 則返回 true
3)public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent 在Activity和View裡分別定義
這個方法嘗試自己處理觸控事件. 如果完成處理 (不需要再交給其他 View 處理), 則返回 true
二,傳遞流程
從Activity的dispatchTouchEvent開始,這是觸控事件的入口,沒有重寫的情況下會無條件的傳遞給根檢視ViewGroup的dispatchTouchEvent,經過如下圖的流程,返回true表示ViewGroup消費了事件,返回false則返回Activity自己的 onTouchEvent() 方法。

這裡寫圖片描述

如果ViewGroup 還有子檢視,且仍然沒有截斷的話,會繼續呼叫子檢視的子檢視,如此遞迴進行。觸控事件派遣的順序是自上而下的。直到到達某個葉子 View (不再有子檢視可以派遣),或者某個 ViewGroup 雖然還有子檢視可以派遣,但其截斷 onInterceptTouchEvent() 方法返回 true。這時,觸控事件真正開始嘗試進行處理。

處理方法:如果註冊了觸控事件監聽器 OnTouchLisener,則優先呼叫 OnTouchLisener.onTouch() 方法,如果沒有註冊監聽器,或OnTouchLisener.onTouch() 方法返回 false,再嘗試呼叫 ViewGroup 自身的 onTouchEvent() 方法

如果該 View 完成了觸控事件的處理 (返回 true),那麼對於其父檢視而言,dispatchTouchEvent() 方法派遣給子檢視的事件圓滿完成,可以向自己的父檢視宣稱完成事件了 (返回 true)。反之,如果該 View 自己沒有完成觸控事件,對於其父檢視 ViewGroup 而言,派遣子檢視並沒有完成事件處理,只好自己處理。如果再次沒有完成,父檢視會向自己的父檢視返回 false,如果各層 ViewGroup 均不能完成事件處理,最終會呼叫 Activity 的 onTouchEvent() 方法,做最後的嘗試。整個實際處理過程順序正好相反,是自下而上的。

三、onClick、onLongClick和onTouchEvent
在 onTouchEvent() 中,會判斷觸控是否構成一次點選事件,從而交給其他一些監聽器,如 onClickListener (監聽點選事件),onLongClickListener (監聽長按事件) 來處理,同時返回 true。

onTouchEvent()方法的判斷:

case MotionEvent.ACTION_DOWN:
    mPrivateFlags |= PRESSED;
    refreshDrawableState();
    if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
         postCheckForLongClick(); //發起CheckForLongPress執行緒的執行,條件都滿足後會設定OnLongClickListener
    }
    break;

case MotionEvent.ACTION_UP:
    if ((mPrivateFlags & PRESSED) != 0) {
         boolean focusTaken = false;
         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
               focusTaken = requestFocus();
         }
    if (!mHasPerformedLongPress) {
       if (mPendingCheckForLongPress != null) {
             removeCallbacks(mPendingCheckForLongPress);
       }

       if (!focusTaken) {
              performClick();//設定OnClickListener
       }
    }
    …
    break;

長按事件順序:onTouch ACTION_DOWN——onLongClick——onTouch ACTION_UP
點選事件順序:onTouch ACTION_DOWN——onTouch ACTION_UP——onClick

onLongClick的發生是在ACTION_UP之前,而onClick的發生是在ACTION_UP後,如果在onLongClick()方法的最後return true,則不會再觸發onClick;如果return false的話會在ACTION_UP後仍然觸發onClick。