Android Touch事件傳遞機制解析

   在網上找了許久有關Android Touch事件分發的文章,發現大部分要不是自己都沒怎麼弄明白就出來發了,就是寫的太底層,最後找到一篇覺得很好的帖子,現在轉載如下:

android系統中的每個ViewGroup的子類都具有下面三個和TouchEvent處理密切相關的方法:

1)public boolean dispatchTouchEvent(MotionEvent ev)          這個方法用來分發TouchEvent

2)public boolean onInterceptTouchEvent(MotionEvent ev)         這個方法用來攔截TouchEvent

3)public boolean onTouchEvent(MotionEvent ev)                 這個方法用來處理TouchEvent

注意:不是所有的View的子類,很多教程都說的是所有的View的子類,只有可以向裏面添加View的控件才需要分發,比如TextView它本身就是最小的view了,所以不用再向它的子視圖分發了,它也沒有子視圖了,所以它沒有dispatch和Intercept,只有touchEvent。

 

說明: 白色爲最外層,它佔滿整個屏幕;

紅色爲中間區域,屬於白色中的一層;

黑色爲中心區域,必於紅色中的一層。

注意:他們本質上是:LinearLayout,而不是RelativeLayout或者其它佈局。

1.由中心區域處理touch事件

佈局文件如下:

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="vertical">

   

        android:id="@+id/view_out"

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"

        android:background="#fff"

10         android:gravity="center">

11            

12                 android:id="@+id/view_mid"

13                 android:layout_width="300px"

14                 android:layout_height="400px"

15                 android:background="#f00"

16                 android:gravity="center">

17            

18                 android:id="@+id/view_center"

19                 android:layout_width="150px"

20                 android:layout_height="150px"

21                 android:background="#000"

22                 android:gravity="center"

23                 android:clickable="true">

24            

25            

26    

複製代碼

注意:                android:clickable="true"  

接下來我們看一下打印的日誌:

結合是上面的日誌,我們可以看一下ACTION_DOWN事件處理流程:

說明:

首先觸摸事件發生時(ACTION_DOWN),由系統調用Activity的dispatchTouchEvent方法,分發該事件。根據觸摸事件的座標,將此事件傳遞給out的dispatchTouchEvent處理,out則調用onInterceptTouchEvent 判斷事件是由自己處理,還是繼續分發給子View。此處由於out不處理Touch事件,故根據事件發生座標,將事件傳遞給out的直接子View(即middle)。

Middle及Center中事件處理過程同上。但是由於Center組件是clickable 表示其能處理Touch事件,故center中的onInterceptTouchEvent方法將事件傳遞給center自己的onTouchEvent方法處理。至此,此Touch事件已被處理,不繼續進行傳遞。

2.沒有指定誰會處理touch事件

佈局文件如下:

27 

28     android:layout_width="fill_parent"

29     android:layout_height="fill_parent"

30     android:orientation="vertical">

31    

32         android:id="@+id/view_out"

33         android:layout_width="fill_parent"

34         android:layout_height="fill_parent"

35         android:background="#fff"

36         android:gravity="center">

37            

38                 android:id="@+id/view_mid"

39                 android:layout_width="300px"

40                 android:layout_height="400px"

41                 android:background="#f00"

42                 android:gravity="center">

43            

44                 android:id="@+id/view_center"

45                 android:layout_width="150px"

46                 android:layout_height="150px"

47                 android:background="#000"

48                 android:gravity="center">

49            

50            

51    

複製代碼

注意:只是比上一次的佈局少了android:clickable="true" 
接下來我們看一下打印的日誌

結合是上面的日誌,我們可以看一下ACTION_DOWN事件處理流程:

說明:

事件處理流程大致同上,區別是此狀態下,所有組件都不會處理事件,事件並不會被center的onTouchEvent方法“消費”,則事件會層層逆向傳遞迴到Activity,若Activity也不對此事件進行處理,此事件相當於消失了(無效果)。

對於後續的move、up事件,由於第一個down事件已經確定由Activity處理事件,故up事有由Activity的dispatchTouchEvent直接分發給自己的onTouchEvent方法處理。

代碼請看最後的附件

總結:

1) Touchevent 中,返回值是 true ,則說明消耗掉了這個事件,返回值是 false ,則沒有消耗掉,會繼續傳遞下去,這個是最基本的。2) 事件傳遞的兩種方式: 
隧道方式:從根元素依次往下傳遞直到最內層子元素或在中間某一元素中由於某一條件停止傳遞。 
冒泡方式:從最內層子元素依次往外傳遞直到根元素或在中間某一元素中由於某一條件停止傳遞。 android對Touch Event的分發邏輯是View從上層分發到下層(dispatchTouchEvent函數)類似於隧道方式,然後下層優先開始處理Event(先mOnTouchListener,再onTouchEvent)並向上返回處理情況(boolean值),若返回true,則上層不再處理。類似於冒泡方式 
於是難題出現了,你若把Touch Event都想辦法給傳到上層了(只能通過返回false來傳到上層),那麼下層的各種子View就不能處理後續事件了。而有的時候我們需要在下層和上層都處理Touch事件

舉個例子,ViewFlipper用來檢測手勢,在內部我們放幾個Image,有點像gallery的效果,也就是左右滑動切換圖片,但是圖片有時候我們希望可以放大縮小!這樣就會存在ViewFlipper裏面需要touch事件,而在image裏面也需要一個touch事件(當圖片大小屏幕邊界的時候可以拖動圖片,而不是左右切換圖片)。

我首先的思路是着手於事件回傳的方式,研究了n久,實際了n久,都沒達到自己想要的結果 ,我甚至於把gallery和gallery3D 的源碼下載下來看了N久也沒辦法去解決,在這裏隨便說一下gallery吧,gallery雖然在這個效果,但是人家並不是ViewFlipper加image這樣來實現的,人家是像遊戲這樣用一個view來統一處理的,我們可以簡單的理解成自定義了一個控件,這樣touch事件想怎麼處理就怎麼處理,不過就是邏輯複雜了,我們想偷懶就沒辦法了,呵呵。。。

最後不停的試啊試啊,想到一個可行的方案,但是我覺得不是很靠譜,也就是:我們在ViewFlipper這裏,我們先把所有的touch都截取到,然後在他的onTouchEvent中,我們先調用imageview的onTouchEvent事件,如果返回true,證明這個事件,imageview要用,那麼ViewFlipper就當什麼事都沒發生,如果imageview返回的false,則調用自己的touchEvent.僞代碼如下:

52 //自定義一個MyViewFlipper 繼承於ViewFlipper,並且實現onTouchEvent方式,

複製代碼

我覺得他不靠譜的原因爲: 1. 他打斷了android的原有的機制,不是很提倡。 
2. 得試先知道ViewFlipper裏面的控件,或者說通過某種路徑能獲取到 
3. 如果ViewFlipper裏面的控件多了,就蛋疼了



最後想說下  csdn的文本編輯真的很垃圾!!!!


發佈了16 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章