android 事件流轉機制

1、android事件的產生

        這一部分比較難,需要涉及android整個架構的基礎知識,包括linux系統對事件的處理。

        這一部分內容來自  android的窗口機制分析------事件處理 。

        由於Android是linux內核的,所以它的事件處理也在linux的基礎上完成的,因此本文我們從linux 內核往應用這個方向慢慢理清它的處理過程。
        linux內核提供了一個Input子系統來實現的,Input子系統會在/dev/input/路徑下創建我們硬件輸入設備的節點,一般情況下在我們的手機中這些節點是以eventXX來命名的,如event0,event1等等,但是如果是虛擬機的話,我們可以看到一個mice,這個mice代表鼠標設備,這是由於PC需要使用鼠標來模擬觸屏。由於這些設備節點是硬件相關的,所以每款設備都是不盡相同的。看到了這些輸入的設備節點,我們可能比較困惑這些eventXX到底代表什麼含義呢,也就是說到底是什麼樣的設備創建了這個節點呢?我們可以從/proc/bus/input/devices中讀出eventXX相關的硬件設備,這裏具體的就不多說了,我們只需要知道android讀取事件信息就是從/dev/input/目錄下的設備節點中讀取出來的,算是android事件處理的起源吧,可以讓大家知道按鍵、觸屏等事件是從哪裏來的,不是我們的重點。
        首先,簡而言之的介紹一下android事件傳遞的流程,按鍵,觸屏等事件是經由WindowManagerService獲取,並通過共享內存和管道的方式傳遞給ViewRoot,ViewRoot再dispatch給Application的View。當有事件從硬件設備輸入時,system_server端在檢測到事件發生時,通過管道(pipe)通知ViewRoot事件發生,此時ViewRoot再去的內存中讀取這個事件信息。
        至於android在事件處理上爲什麼使用共享內存而不是直接使用Binder機制,我的猜測應該是google爲了保證事件響應的實時性,因此在選擇進程間傳遞事件的方式中,選擇了高的共享內存的方式,由於共享內存在數據管理過程中基本不涉及到內存的數據拷貝,只是在進程讀寫時涉及到2次數據拷貝,這個是不可避免的數據拷貝,因此這種方式能夠很好的保證系統對事件的響應,但是僅僅是共享內存是不夠的,因爲共享內存的通信方式並不能夠通知對方有數據更新,因此android在事件處理過程中加入了另一種進程間通信方式管道(pipe),管道的效率不如共享內存高,會不會影響事件處理的實時性?沒關係,每次system_serve通知ViewRoot只是向其傳遞一個字符,即輕巧有簡單,一個字符的多次數據拷貝,我想google還是能夠接受的。
        好的,瞭解了一些基本知識後,我們從底層往上層來分析事件的傳遞過程,這裏爲了下文便於理解,首先列出整個事件處理的結構圖。


       具體內容可以去看該博客。

      看到這裏,讀者需要明確,android的事件由此產生,並且最終傳遞到viewroot。

2、android的事件模型

       博客Android中的事件模型爲我們指出了android的事件模型,在此對其進行引用。

       以前寫 android ,對事件的處理沒有太深入,只是簡單的 onTouchEvent 就 ok 了,現在寫的 UI ,很多自定義組件,父 view 和子 view 都需要接收事件,然後處理。如果不弄明白它的事件傳遞機制,很難擁有好的用戶體驗。
       Touchevent 中,返回值是 true ,則說明消耗掉了這個事件,返回值是 false ,則沒有消耗掉,會繼續傳遞下去,這個是最基本的。
在 View 中跟 Touch 相關的事件有 dispatchTouchEvent , interceptTouchEvnet , onTouchEvent 三種。 dispatchTouchEvent 是負責分發事件的,事件從 activity 傳遞出來之後,最先到達的就是最頂層 view 的 dispatchTouchEvent ,然後它進行分發,如果返回 false ,則交給這個 view 的 interceptTouchEvent 方法來決定是否要攔截這個事件,如果 interceptTouchEvent 返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理,如果 interceptTouchEvent 返回 false ,那麼就傳遞給子 view ,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發。
       如果事件傳遞到某一層的子 view 的 onTouchEvent 上了,這個方法返回了 false ,那麼這個事件會從這個 view 往上傳遞,都是 onTouchEvent 來接收。而如果傳遞到最上面的 onTouchEvent 也返回 false 的話,這個事件就會“消失”,而且接收不到下一次事件。(我說的一次事件指的是 down 到 up 之間的一系列事件)
       我畫了個圖,見附件。


       總結一下,如果這一次事件沒有人消耗掉,則系統不會給你下一次事件,因爲他會認爲你這次的事件阻塞了,沒必要給下一次。onTouchEvent如果不消耗的話,會從子view傳遞到父view。

3、事件傳遞具體例子

        onInterceptTouchEvent()用於處理事件並改變事件的傳遞方向。處理事件這個不用說了,你在函數內部編寫代碼處理就可以了。而決定傳遞方向的是返回值,返回爲false時事件會傳遞給子控件的onInterceptTouchEvent();返回值爲true時事件會傳遞給當前控件的onTouchEvent(),而不在傳遞給子控件,這就是所謂的Intercept(截斷)。
        onTouchEvent() 用於處理事件,返回值決定當前控件是否消費(consume)了這個事件。可能你要問是否消費了又區別嗎,反正我已經針對事件編寫了處理代碼?答案是有區別!比如ACTION_MOVE或者ACTION_UP發生的前提是一定曾經發生了ACTION_DOWN,如果你沒有消費ACTION_DOWN,那麼系統會認爲ACTION_DOWN沒有發生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲。
        本文源地址:http://www.cnblogs.com/rocky_yi/archive/2011/01/21/1941522.html# ,轉載請註明出處!

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent" >  
  6.     <com.touchstudy.LayoutView2  
  7.         android:orientation="vertical"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:gravity="center">  
  11.        <com.touchstudy.MyTextView   
  12.             android:layout_width="wrap_content"  
  13.             android:layout_height="wrap_content"  
  14.             android:id="@+id/tv"  
  15.             android:text="AB"  
  16.             android:textSize="40sp"  
  17.             android:textStyle="bold"  
  18.             android:background="#FFFFFF"  
  19.             android:textColor="#0000FF"/>  
  20.    </com.touchstudy.LayoutView2>  
  21. </com.touchstudy.LayoutView1>  
        在沒有重寫onInterceptTouchEvent()和onTouchEvent()的情況下(他們的返回值都是false), 對上面這個佈局,MotionEvent事件的傳遞順序如下:


        當某個控件的onInterceptTouchEvent()返回值爲true時,就會發生截斷,事件被傳到當前控件的onTouchEvent()。如我們將LayoutView2的onInterceptTouchEvent()返回值爲true,則傳遞流程變成:


        如果我們同時將LayoutView2的onInterceptTouchEvent()和onTouchEvent()設置成true,那麼LayoutView2將消費被傳遞的事件,同時後續事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)會直接傳給LayoutView2的onTouchEvent(),不傳給其他任何控件的任何函數。同時傳遞給子空間一個ACTION_CANCEL事件。傳遞流程變成(圖中沒有畫出ACTION_CANCEL事件):

        

附SDK給出的說明:
·  You will receive the down event here.
·  The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
·  For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
·  If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.

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