天天看點

Android中事件傳遞機制的總結

事件傳遞雖然算不上某個單獨的知識點,但是在實際項目開發中肯定會碰到,如果不明白其中的原理,那在設計各種滑動效果時就會感到很困惑。

關于事件的傳遞,我們可能會有以下疑問:

事件是如何傳遞的 事件是如何處理的 自定義view的時候,事件也沖突了怎麼解決

帶着這三個疑問,我們來總結一下事件傳遞機制是怎麼回事。

一、事件分發的原理:

1、事件是如何傳遞的:

(1)首先由activity分發,分發給根view,也就是decorview(decorview為整個window界面的最頂層view) (2)然後由根view分發到子的view

如下圖所示:

Android中事件傳遞機制的總結

再來看下面這張圖:(這張圖是整個事件傳遞機制的核心)

Android中事件傳遞機制的總結

上圖顯示:

  在viewgroup中可以通過onintercepttouchevent方法對事件傳遞進行攔截。onintercepttouchevent方法:

    傳回true代表不允許事件繼續向子view傳遞,将會觸發目前view的ontouchevent(),進行事件的消費;

    傳回false代表不對事件進行攔截,事件可以傳遞給孩子

    預設傳回false

2、事件是如何處理的:

Android中事件傳遞機制的總結

再來看下面這張圖:

Android中事件傳遞機制的總結

上圖顯示:子view中如果将傳遞的事件消費掉,父類的viewgroup中将無法接收到任何事件。

二、ontouch和onclick事件同時發生的問題:

首先這裡要解釋一下各種概念,避免混淆。

1、各種概念:

事件:

  混合體(可能是點選事件也可能是觸摸事件)。

觸摸事件:

  按下、滑動和離開

點選事件:

  按下、停留一會兒和離開

觸摸ontouch事件和點選onclick事件有什麼關系?

(1)執行先後不一樣。觸摸事件先執行 (2)觸摸事件傳回值影響點選事件(前者影響後者,而後者不影響前者)

2、ontouch和onclick事件同時執行:

    如果按鈕的ontouch和onclick方法同時執行,會有什麼效果呢?我們通過代碼來看一下:

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

上方代碼中,按鈕btn既包含了ontouch事件,也包含了onclick事件,現在運作程式,點選按鈕,背景列印的日志如下:

Android中事件傳遞機制的總結

通過上方日志我們可以看到,ontouch事件是比onclick事件先執行的。

備注:這裡提示一下,如果我們僅僅隻是用手指點選按鈕,然後馬上松開,ontouch事件中隻會執行action_down和action_up動作;如果用手機點選按鈕,并且手指還在按鈕上滑動了一會兒,那麼滑動的過程中,action_move動作就會不停的執行。現在我們應該能明白這三個動作的含義了吧?

3、隻執行ontouch事件,不執行onclick事件:

如果按鈕的ontouch和onclick方法同時執行,在有些情況下不太滿足産品的需求。那如果隻想執行ontouch事件,不執行onclick事件,該怎麼做呢?很簡單,隻需要在上方代碼中,将第39行的代碼改為return true,就行了,即:将ontouch方法的傳回值改為true,就會隻執行ontouch事件,不執行onclick事件。改完代碼之後,背景的運作效果如下:

Android中事件傳遞機制的總結

為什麼這樣改代碼就可以了呢?這就需要從源碼的角度來了解了。

button按鈕中沒有dispatchtouchevent方法,需要去它的父類view.java中去找dispatchtouchevent方法。源碼如下所示:

Android中事件傳遞機制的總結

上圖分析:紅框部分的ontouch()方法預設是傳回false,是以就會執行藍框部分的代碼,即:調用ontouchevent()方法。而ontouchevent方法中,會在action_up動作裡面會去初始化onclick事件。如下圖所示:

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

于是onclick事件就得到了執行。

三、onclick和onlongclick事件能同時發生:

我們通過代碼來示範一下。

1、ontouch事件、onlongclick事件、onclick事件預設是同時執行:(執行的先後順序:ontouch > onlongclick > onclick)

完整版代碼如下:

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

運作程式後,長按按鈕,背景日志如下:

Android中事件傳遞機制的總結

源碼比較長,就不貼出來了,通過檢視源碼我們得知,當ontouch事件中的action_down動作執行180ms之後,就會執行onlongclick事件。

那我們現在知道了,如果在一個按鈕上按下的時間過長,onlongclick事件會比onclick事件先執行。

2、隻執行ontouch事件和onlongclick事件,不執行onclick事件:

為了實作這種邏輯,也很簡單,隻需要将上方的第50行代碼改為return true就行了,即:将onlongclick方法的傳回值改為true,就不會執行onclick事件了。改完代碼之後,背景的運作效果如下:

Android中事件傳遞機制的總結

為什麼這樣改代碼就可以了呢?這就需要從源碼的角度來了解了。在view.java中的dispatchtouchevent方法裡,action_up動作裡面對onlongtouch事件進行了處理,具體源碼就不展示出來了,這個有點複雜。

四、事件傳遞機制調用順序:

viewgroup的事件傳遞方法:

dispatchtouchevent

onintercepttouchevent

ontouchevent

view的事件傳遞方法:

view的dispatchtouchevent

view的ontouchevent

注意,隻有父的viewgroup容器才有onintercepttouchevent方法。這也很好了解,最小的那個子的view沒必要再攔截了,因為無法繼續向下傳遞事件,是否攔截已經沒有意義了。

接下來,我們用linearlayout代表viewgroup,用button代表子view,然後去重寫linearlayout和button中的事件傳遞方法,看一下各個方法的調用順序。代碼如下:

(1)mylinearlayout.java:(重寫linearlayout中的事件傳遞方法)

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

(2)mybutton.java:(重寫button中的事件傳遞方法,注意:這裡面沒有onintercepttouchevent方法)

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

上方代碼中,将ontouchevent方法的傳回值修改為true(59行),表示這個子的view希望消費這個事件。

(3)activity_main.xml:

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

<code>上面的xml中,将我們自定義的mylinearlayout和mybutton用上了。</code>

(4)mainactivity.java:

Android中事件傳遞機制的總結
Android中事件傳遞機制的總結

分析之前,我們先記住下面這句話:(記住這句話,分析下面的日志就好了解了)

  在android中,一切事件處理的開始都是從down事件開始的,如何你處理了down事件,其他的事件就都收不到了。

1、按照上面的代碼,背景日志如下:

Android中事件傳遞機制的總結

通過上圖的箭頭處可以看到,事件是傳遞給了子view去消費。

2、上面的代碼中,将mylinearlayout.java中ontouchevent方法傳回值修改為true,将mybutton.java中的ontouchevent方法傳回值修改為false,背景日志如下:

Android中事件傳遞機制的總結

通過上圖的箭頭處可以看到,事件是傳遞給了子view,子view說它不消費了,于是又回傳給父的viewgroup,viewgroup消費了這個事件。

3、上面的代碼中,将mylinearlayout.java中ontouchevent方法傳回值修改為true,将mylinearlayout.java中onintercepttouchevent方法傳回值修改為true,背景日志如下:

Android中事件傳遞機制的總結

通過上圖的箭頭處可以看到,此時viewgroup已經将事件攔截了,是以根本就不會傳遞給子的veiw,父的viewgroup自己把事件給消費掉了。

繼續閱讀