Android 觸摸事件處理機制

  Android 觸摸事件的處理主要涉及到幾個方法:onInterceptTouchEvent(), dipatchTouchEvent(), onTouchEvent(), onTouch()。this

  onInterceptTouchEvent() 用於攔截事件並改變事件傳遞方向。解釋一下事件傳遞。好比一個Activity中展現給用戶多是ViewGroup和View的多層嵌套,默認狀況下觸摸事件產生以後從最外層一次傳遞到最裏面一層,而後在從最裏面一層開始響應。從最裏面一層開始依次調用各層次的dispatchTouchEvent()進行分發,dispatchTouchEvent()中在調用onTouch / onTouchEvent進行響應觸摸事件。spa

  onInterceptTouchEvent() 方法能夠將觸摸事件的傳遞截斷,讓觸摸事件在某一層就不往下面傳遞,就開始調用這一層的dispatchTouchEvent(),開始向上層返回。若是須要在某一層攔截,須要複寫該層的onInterceptTouchEvent()方法,並讓該方法返回 true。經過一張圖來解釋一下。
rest

  

  假設一個Activity展現的界面有A->B->C->D四層,當事件發生以後,首先通過A的onInterceptTouchEvent(), 若是A的onInterceptTouchEvent()返回false,則會傳遞到B的onInterceptTouchEvent(),若是返回false則一次向下(內層)傳遞。若是某一層的onInterceptTouchEvent()返回true,而後就會調用該層的disatchTouchEvent()分發事件,事件再也不向下傳遞。若是各層都沒有攔截事件則從最內層開始調用dispatchTouchEvent(),若是某一各層的dispatchTouchEvent()返回true,則代表該層消費了該事件,則上面層的dispatchTouEvent()不會被調用。舉一個例子:code

  

  上圖中B層的onInterceptTouchEvent()返回true,則事件被攔截,開始調用B層的dispatchTouchEvent()向上返回一次響應觸摸事件。orm

  知道這個機制有什麼卵用嗎?blog

  一個簡單例子,咱們在scrollView中放置了圖片,圖片容許縮放拖動,可是你對圖片進行拖動的時候會發現scrollView也跟着動,這樣體驗就會很很差,怎麼辦呢?事件

  結合上面的分析,咱們能夠知道,若是讓觸摸事件傳遞到內層的圖片,而後在在圖片的disPatchTouchEvent()中把這個觸摸事件消費了就不就能夠了嗎?圖片

  具體作法在圖片的onTouch() 方法中,ACTION_DOWN中設置scrollView不攔截事件,經過scrollView.requestDisallowInterceptTouchEvent(true)來完成,完成想要的處理以後在圖片的onTouch()方法最後返回true就能夠實現了。ip

  問題又來了 onTouch(View v, MotionEvent event) 和 onTouchEvent(MotionEvent event) 有什麼區別呢? 看起來那麼像,不會是完成相同的功能吧?這樣作不是畫蛇添足嗎,爲何不用一個就行了。開發

  爲了搞清楚這個問題,首先須要來看View中disPatchTouchEvent()方法:

public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first.
    if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event.
        if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch.
        event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture
 stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture.
    if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }

  從上面能夠看出onTouch() 先於 onTouchEvent()執行。經過查看View的源碼還發現,或者簡單推理一下咱們設置setOnTouchListener()設置的是誰就知道,咱們何時須要調用onTouch()方法讓其發揮做用而不是讓onTouchEvent()來響應了。View源碼中能夠看出onTouch是一個Interface的方式實現,將處理邏輯的實現交給開發者自定義,所以能夠得出Android系統自帶的控件咱們使用onTouch處理事件,若是咱們須要擴展View,則須要複寫onTouchEvent()來實現事件的處理。其實onTouch(View v, MotionEvent event) 和 onTouchEvent(MotionEvent event)能夠從這兩個方法接受的參數就能夠看出不一樣來,onTouch包含一個View類型的參數,所以是能夠設置給某個View的,onTouchEvent()則是給某個View本身用的。

  既然提到了View的事件響應,那onClick事件又是怎麼響應的呢? 經過產看源碼能夠發現onClick是在onTouchEvent中執行的,並且是在onTouchEvent的ACTION_UP事件中執行的。所以若是View 的onTouch()返回true則會致使onClick得不到執行,由於onTouchEvent()得不到執行。

  此外Activity中也有onTouchEvent()成員方法,若是Activity中的View都不處理Event則Activity的onTouchEvent()會調用。

相關文章
相關標籤/搜索