詳解Android 觸摸事件處理和傳遞過程的來龍去脈

前言

前面有兩篇博客從源碼角度分析了Android中View的觸摸事件處理和ViewGroup的觸摸事件傳遞過程,對於初學者來說可能直接從源碼瞭解Android觸摸事件有點門檻,那麼這篇博客我們不分析源碼,儘量用簡介的語言和簡單的例子來詳細解釋Android觸摸事件的處理和觸摸事件傳遞的過程。

Android觸摸事件流程簡述

這裏我將觸摸事件理解爲兩部分:

  1. 觸摸事件的傳遞過程
  2. 觸摸事件的處理過程

觸摸事件的傳遞過程

我們知道Activity的視圖結構是自上而下的樹形結構,而一個典型的觸摸事件是從Activity開始,傳遞到Activity的根視圖,然後經過層層ViewGroup,最後傳遞給某一個具體的View或者ViewGroup去處理。這就是觸摸事件的傳遞過程,所以Android觸摸事件的傳遞過程是自上而下的。猶如這麼一個過程Activity–>ViewGroup–>View,只有上一個視圖沒有中斷該觸摸事件傳遞,下一個視圖才能接收到該觸摸事件。那麼疑問來了,什麼情況下觸摸事件會中斷傳遞呢?什麼時候不中斷傳遞?稍後解釋。

觸摸事件的處理過程

而觸摸事件的處理過程卻是自下而上的,怎麼理解呢? 我們先建一個模型,比如有一個觸摸事件傳遞過程如下:Activity–>ViewGroup–>View,那麼此次觸摸事件最先由View來處理,如果View沒有處理掉該觸摸事件,則由其父視圖ViewGroup來處理該觸摸事件,如果ViewGroup還沒有處理掉該觸摸事件,最後由當前Activity來處理該觸摸事件。因此觸摸事件的處理是自下而上的,只有下面的視圖沒有處理掉此次觸摸事件,上一層視圖纔有機會去處理該觸摸事件。那麼疑問來了,什麼情況下觸摸事件處理掉了?什麼情況下又沒有處理掉?稍後解釋。

觸摸事件相關的方法

我們知道在View中有dispatchTouchEvent和onTouchEvent兩個方法和觸摸事件相關,其實在Activity中同樣也有這兩個方法。而在ViewGroup中除了以上兩個方法之外還有一個onInterceptTouchEvent方法用於是否攔截觸摸事件。而以上三個方法的返回值都是布爾類型,返回值決定觸摸事件的傳遞是否被攔截,或者觸摸事件是否被處理掉。這裏直接給出一個結論就是當前視圖的dispatchTouchEvent方法返回值爲false表示觸摸事件沒有被處理掉,他的父視圖就該去處理此次觸摸事件,相反當返回值爲true時,表示該視圖處理掉了觸摸事件,其父View就沒有機會處理觸摸事件了。如果ViewGroup中的onInterceptTouchEvent返回值爲true表示攔截觸摸事件,其子View就無法接收到觸摸事件,也就沒有機會去處理觸摸事件了。系統默認onInterceptTouchEvent方法返回false,表示不攔截觸摸事件。

View的觸摸事件

在View中與觸摸事件有關的方法有dispatchTouchEvent,onTouch和onTouchEvent方法。觸摸事件會先調用dispatchTouchEvent方法,如果用戶設置了onTouchListener觸摸監聽事件的話再調用onTouch方法,最後當onTouch方法返回false時,纔會去調用onTouchEvent方法,否則觸摸事件在View裏面處理結束。而onTouch和onTouchEvent方法的返回值會決定dispatchTouchEvent方法的返回值。情況如下:

1.如果用戶設置了onTouchListener觸摸監聽,且監聽的回調方法返回爲true時,該View的觸摸事件處理結束,dispatchTouchEvent方法返回true。

2.如果用戶未設置觸摸監聽事件或者監聽回調方法返回false,則dispatchTouchEvent的返回值由 onTouchEvent方法的返回值決定。onTouchEvent方法返回true則dispatchTouchEvent方法返回false,反之。

dispatchTouchEvent方法的返回值決定當前View是否消費掉此次觸摸事件。如果dispatchTouchEvent返回true表示此次觸摸事件消費掉了,其父View就沒有機會來處理該觸摸事件了。如果dispatchToucEvent返回false,則表示該View沒有消費掉此次觸摸事件,該觸摸事件會交給其父View來處理。

這裏寫圖片描述

ViewGroup觸摸事件

由於ViewGroup繼承自View,ViewGroup除了有dispatchTouchEvent和onTouchEvent方法還有一個onInterceptTouchEvent方法,該方法用於是否攔截觸摸事件。

用戶可以重寫該方法,當返回true時,表示攔截此次觸摸事件,不讓觸摸事件向下傳遞,也就是它的子View無法接收到觸摸事件,也就無法處理觸摸事件了。當返回false時,表示不攔截觸摸事件,讓其子View可以接收到觸摸事件並有機會處理。

值得注意的是,如果其子視圖不想被其父視圖攔截觸摸事件怎麼辦?android給子視圖提供了getParent().requestDisallowInterceptTouchEvent(true);方法用來請求父視圖不攔截該該子視圖的觸摸事件,也就是不管父視圖ViewGroup的onInterceptTouchEvent方法返回什麼值,該子視圖都能接收到觸摸事件。

在ViewGroup中重寫View中的dispatchTouchEvent方法,該方法用於分發觸摸事件,如果onInterceptTouchEvent方法返回false,則觸摸事件就會傳遞給其子View的dispatchTouchEvent方法處理。

而如果其子View消費掉了此次觸摸事件則該觸摸事件到此結束,ViewGroup沒有機會去處理該觸摸事件了。

如果其子View沒有消費掉此次觸摸事件,則觸摸事件由其自己處理,ViewGroup就會調用其父類中的dispatchTouchEvent方法,由於ViewGroup的父類是View,所以此處的處理過程和View觸摸事件處理過程一樣。

這裏寫圖片描述

Activity的觸摸事件

你是否到現在還不知道Android處理觸摸事件的入口在哪裏?其實整個Android觸摸事件處理入口在Activity類中的dispatchTouchEvent方法中,只不過我們幾乎不去重寫Activity#dispatchTouchEvent方法,那麼今天我們簡單瞭解下Activity的觸摸事件傳遞和處理。Activity類中也有和View同樣的方法dispatchTouchEvent和onTouchEvent兩個方法。

1.Activity#dispatchTouchEvent方法是觸摸事件的入口,用於接收用戶觸摸事件,並且將觸摸事件分發給當前Activity的根視圖。
2.Activity#onTouchEvent方法在Activity的根視圖沒有消費掉此次用戶的觸摸事件時調用。

那麼什麼時候Activity的觸摸事件會傳遞下去呢?只要我們不去重寫Activity的dispatchTouchEvent方法,觸摸事件是會順利傳遞下去的。也就是說Android系統默認在Activity中無條件執行dispatchTouchEvent方法來接受觸摸事件,除非你重寫該方法。言外之意就是當你想攔截整個觸摸事件傳遞過程時,你可以重寫Activity中的dispatchTouchEvent方法。

那麼什麼時候Activity可以處理此次的觸摸事件呢?我們知道只有下一層視圖沒有處理掉該觸摸事件Activity纔有機會。也就是ViewGroup的dispatchTouchEvent方法返回false時,Activity纔會去調用onTouchEvent方法處理觸摸事件。

總結

在沒有 重寫任何方法的情況下,觸摸事件的派遣將不會被截斷,從 Activity 的根視圖,自上而下的派遣到葉子 View ,然後調用該 View 的 onTouchEvent() (如果註冊了監聽器的話,則優先調用 OnTouchListener.onTouch() ,返回 false 纔會再調用 onTouchEvent() )。如果該 View 不能處理事件( onTouchEvent() 返回了 false ),其父視圖繼續嘗試處理,直到最後,調用 Activity 的 onTouchEvent() 方法。另外,值得注意的是,如果沒有響應一個手勢的開始事件 ( ACTION_DOWN ),則不會接到該手勢的後續事件。

View和ViewGroup觸摸事件處理傳遞詳細篇請看我的另外兩篇博客:
Android View觸摸事件傳遞機制
Android ViewGroup 觸摸事件傳遞機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章