Android觸摸事件傳遞機制


    簡單梳理一下Android觸摸事件傳遞機制的知識點。

一、View與ViewGroup的關系

    View和ViewGroup二者的繼承關系如下圖所示:

    View是Android中最基本的一種UI組件,它是所有控件類的基類。View類的作用是可以在屏幕上繪制一塊矩形區域,並能響應這塊區域的各種事件(如觸摸事件、點擊事件等)。我們平時使用的各種控件其實都是繼承自View類,在View的基礎上又添加了一些特有的功能。比如TextView可以用於顯示文本,進一步還能拓展(extends)為可編輯的文本控件——EditText類或者可點擊的文本控件——Button類。

    ViewGroup也是繼承自View類,但它是一種非常特殊的View,因為它可以作為一個容器來放置其他的控件或布局。我們常用的各種布局Layout類以及ListView、ScrollView等都是繼承自ViewGroup。

    在討論Android觸摸事件傳遞機制時,我們這里說的View特指除了ViewGroup以外的View控件,也就是無法作為容器的最小UI單位。

二、Android UI層次結構

    Android UI層次結構圖和類圖如下圖所示:

 

                                                                                       

     

  Activity是Android應用程序的門面和載體,它代表一個完整的用戶界面。Activity提供了一個窗口來繪制各種視圖,即PhoneWindow類。該類繼承自頂層窗口類Window,並且包含一個DecorView類對象。DecorView繼承自FrameLayout(幀布局),所以本質上是一個ViewGroup,而且是當前活動所放置的全部View的根視圖(RootView)。當我們創建一個活動時,在活動的onCreate()方法中調用setContentView(R.layout.layout_name)方法就是為該活動的ContentView部分指定布局內容從而完成GUI的渲染。

三、事件的類型

    事件主要分為觸摸事件和點擊事件。

    1、觸摸事件:對應的是MotionEvent類,主要有以下三種類型:

    ACTION_DOWN:表示用戶手指按下的動作,標志着觸摸事件的開始。

    ACTION_UP:表示用戶手指離開屏幕的動作,標志着觸摸事件的結束。

    ACTION_MOVE:表示用戶手指移動的動作。當用戶手指按下屏幕后,在松開之前,只要移動的距離超過了一定的閾值即判定為ACTION_MOVE動作。實際上,即使是手指非常   輕微的移動也會被系統監測到從而判定為ACTION_MOVE動作。

    ps:用戶觸摸屏幕操作由ACTION_DOWN事件開始,結束於ACTION_UP事件,可以有0次或多次ACTION_MOVE事件。

 

    2、點擊事件:用戶手指按下→停留若干時間(可長可短)→用戶手指松開,這一完整的過程視為一次點擊事件。可以看出,觸摸事件先於點擊事件執行。

四、觸摸事件傳遞機制

    該機制主要包含三個角色、三個階段和三個方法。

    1、三個角色分別為:Activity、View、ViewGroup。

    2、三個階段分別為:分發(dispatch)、攔截(intercept)和消費(consume)。

    3、三個方法的方法原型分別為:

    ①public boolean dispatchTouchEvent(MotionEvent e)……對應於分發事件

    ②public boolean onInterceptTouchEvent(MotionEvent e)……對應於攔截事件

   ③public boolean onTouchEvent(MotionEvent e)……對應於消費事件(即處理事件)

    4、解釋:

    由前面的Android UI層次結構分析可以看出來,一個Activity包含多層視圖:先是窗口(PhoneWindow),接着是根視圖(即DecorView),然后是具體的ContentView;ContentView視圖部分又是各種布局和控件的嵌套組合。那么,當用戶做出了觸摸動作時,觸摸事件應當由哪一個View或者哪一個ViewGroup負責處理呢?這就是觸摸事件傳遞機制要解決的問題。為了方便,下面會用視圖這一詞代替View或ViewGroup對象。

    ①在Android系統中,所有觸摸事件必須都由dispatchTouchEvent()方法判斷是否進行分發。分發事件相當於“傳遞事件”的一個過程,分發事件的目的就是為了找到正確的視圖來處理這個觸摸事件。而處理事件的過程就是所謂的消費階段,調用的方法是onTouchEvent()。如果確定要繼續分發事件,向什么地方分發呢?如果是ViewGroup,則是向自己的子視圖進行分發;如果是View,向子視圖進行分發本質上其實就是分發給自身。如果dispatchTouchEvent()方法返回true,則表示事件由當前視圖直接進行消費(處理),不再繼續分發;否則,向子視圖進行分發。

    ②除了分發和消費,還有一個攔截操作。須注意的是:只有ViewGroup可以攔截觸摸事件,Activity和View只能分發事件和消費事件。這一點其實非常好理解:因為View沒有子視圖可言,所以不存在攔截的操作;而ViewGroup具有子視圖,選擇性地進行攔截事件是有意義的。ViewGroup可以調用onInterceptTouchEvent()方法來判斷是否攔截該觸摸事件。如果該方法返回true,表示攔截該事件不再分發給子視圖;否則,表示不攔截事件繼續分發給子視圖。須注意,ViewGroup類默認是不攔截任何觸摸事件的,如果有需要可以重寫其onInterceptTouchEvent()方法進行選擇性地攔截。

    ③一旦確定由某個視圖負責處理(即消費)觸摸事件ACTION_DOWN,那么后續的ACTION_MOVE和ACTION_UP等觸摸事件也會同樣被該視圖捕獲。在消費事件階段,該視圖會調用onTouchEvent()方法。如果該方法返回true,那么表示事件被成功消費;如果返回false,那么意味着當前視圖沒有能力消費該觸摸事件,事件會拋給上層的視圖調用其onTouchEvent()方法進行處理。

 

    5、觸摸事件傳遞過程:

    考慮最一般的情況,即觸摸事件ACTION_DOWN由某個View消費掉的情況。事件傳遞過程分成兩步,整個過程都嚴格遵循上述的分發、攔截和消費的原理。如下圖所示:

                                   

  

    ①第一步:先由Activity傳遞到ViewGroup,具體地講就是Activity→PhoneWindow→DecorView(根視圖)。

    ②第二步:由根視圖DecorView通過一層層的ViewGroup傳遞到某個子View,由該View負責消費該事件以及后續的事件。

    至於其他的情況,比如該View捕獲到事件后無法消費拋給上層視圖或者某個ViewGroup捕獲到事件,則參考下圖:

 

 

五、something more

    除了分發、攔截和消費這三個方法外,還可以在活動中為View類設置監聽器接口:

    監聽觸摸事件:setOnTouchListener(),須重寫接口的onTouch()方法。

    監聽點擊事件:setOnClickListener(),須重寫接口的onClick()方法。

    監聽長按事件:setOnLongClickListener(),須重寫接口的onLongClick()方法

    觀察View類的源碼,有以下結論:

    ①View的onTouch()方法會先於onTouchEvent()方法執行,並且當前僅當onTouch()方法返回false時,才會執行onTouchEvent()。

    ②只有當onTouchEvent()方法得到調用,並且觸摸事件是ACTION_UP時,onClick()方法才會被調用。這一點邏輯上也非常容易理解,因為當用戶手指按下屏幕,只有當他手指離開屏幕觸發事件ACTION_UP后才算是一個點擊事件。也就是說,當一個View確定捕獲觸摸事件時,onTouch()方法會介於分發方法和消費方法之間執行,並且只有當其返回false時,后續的消費方法onTouchEvent()才能執行。而只有onTouchEvent()方法成功執行返回true,才能表示觸摸事件被正確處理,如果是ACTION_UP被正確處理,那么就會調用onClick()方法。

    ③這三個監聽方法的執行順序是:onTouch()方法→onLongClick()→onClick() 原因:當用戶按下手指后,先執行觸摸監聽方法;如果用戶按下時間超過一個閾值還沒有松手,會執行長按監聽方法;如果用戶最后松開手指,最后執行點擊監聽方法。並且這三個方法是后者依賴於前者的返回值!只有onClick()返回false,onLongClick()才能執行;只有onLongClick()返回false,onClick()才能執行。

.............................................................................................................................................................over

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM