Android View 事件傳遞機制

jopen 9年前發布 | 16K 次閱讀 Android Android開發 移動開發

View事件傳遞 touch事件分發

學習Android一年有余,今天開始以自己的理解去介紹一下Android開發常用到的基礎技術。第一個介紹的是View 的事件傳遞機制,建議首先去看一下[codeKK中關于View事件傳遞介紹的文章][1],我這里只是對這個的補充以及自己理解。有不正確的地方歡迎拍 磚?。

第一部分:知識儲備

  1. 在Android中所有的TouchEvent事件都封裝在MotionEvent對象中,而TouchEvent事件主要包括Touch的位置,時間,歷史記錄,滑動等等。
  2. 常見MotionEvent的類型包括:ACTION_DOWN,ACTION_MOVE,ACTION_CANCEL,ACTION_UP,ACTION_SCROLL等等。
  3. 我們關心的對事件的處理實質上就是 dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent這三個函數,分別代表事件傳遞,事件消費 和事件攔截的意思,這三個函數都是boolean類型的,如果返回值為true代表該事件被消耗。

    (注明:以上的內容是從codeKK文章中部分摘取的)

    </blockquote> </li> </ol>

    第二部分:實戰演練

    以我的理解,要掌握整個事件的傳遞過程,必須得理清楚那三個事件處理函數,最好是自己寫寫代碼驗證一下。 以下我根據事件的消耗分四個部分:不攔截不消費,不攔截消費,攔截不消費,攔截消費。為了更好的理解,假設布局層次如下:

    Android View 事件傳遞機制

    這個是在UI Automator中的截圖,布局很簡單,最外層為一個LinearLayout,然后嵌套了兩個LinearLayout。在這個里為了方便我簡稱為從最外到里分別為L0,L1,L2。其預覽圖如下:

    Android View 事件傳遞機制

    布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.TouchEventDemo.MyLinearLayout0
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:id="@+id/L0"
            android:background="@android:color/white"
            >
        <com.example.TouchEventDemo.MyLinearLayout1
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/L1"
                android:layout_margin="20dp"
                android:background="@android:color/holo_blue_dark"
                >
            <com.example.TouchEventDemo.MyLinearLayout2
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/L2"
                    android:background="@android:color/holo_red_light"
                    android:layout_margin="20dp"
                    android:orientation="horizontal"
                    />
        </com.example.TouchEventDemo.MyLinearLayout1>
    </com.example.TouchEventDemo.MyLinearLayout0>

    這里為了更好的看清楚事件的傳遞,我們需要自定義三個LinearLayout,并重寫三個事件函數,dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent
       @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.i(TAG, "MyLinearLayout0------------dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.i(TAG, "MyLinearLayout0------------onTouchEvent");
            return super.onTouchEvent(event);
        }
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.i(TAG, "MyLinearLayout0------------onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
        }

    好了,前期準備工作已經完成,我們開始驗證:

    1.不攔截不消費:

    即所有的onTouchEvent,onInterceptTouchEvent函數返回為fasle。預期的流程應該是:

    Android View 事件傳遞機制

    當我們點擊L2即紅色區域程序運行的結果是:

    Android View 事件傳遞機制

    可以看到和我們預期的一模一樣。

    2.不攔截消費:

    這里假定L1對事件不攔截消費,即L1.onInterceptTouchEvent位false,L1.onTouchEvent返回為true,說明L1對消費了事件,且沒有攔截事件。同樣我們的預期流程應該有兩種情況: 第一種:L2消費掉事件,即L2.onTouchEvent返回為true,其流程應該是:

    Android View 事件傳遞機制

    當我們點擊L2即紅色區域程序運行的結果是:

    Android View 事件傳遞機制

    第二種:L2沒有消費掉事件,即L2.onTouchEvent返回為fasle,其流程應該是:

    Android View 事件傳遞機制

    當我們點擊L2即紅色區域程序運行的結果是:

    Android View 事件傳遞機制

    到這里前兩種情況基本介紹完了,總結下,在這里最重要的一點要記住,如果子VIEW沒有消費掉ACTION_DOWN的事件,則后續的UP/MOVE事件將不會到來。

    </blockquote>

    3.攔截不消費:

    這里假定的對象還是L1,即L1對事件攔截但不消費,即L1.onInterceptTouchEvent位true,L1.onTouchEvent返回為false。從字面上可以L1攔截了事件,則L2是不會在接受到任何事件的。預期的流程應該是:

    Android View 事件傳遞機制

    當我們點擊L2即紅色區域程序運行的結果是:

    Android View 事件傳遞機制

    可以看到和我們流程圖是一樣的。

    4.攔截消費:

    這里假定的對還是L1,即L1對事件攔截且消費,即L1.onInterceptTouchEvent位true,L1.onTouchEvent返回為true。預期的流程應該是:

    Android View 事件傳遞機制

    當我們點擊L2即紅色區域程序運行的結果是:

    Android View 事件傳遞機制

    運行結果和流程圖一樣

    第三部分:總結

    以上就是就是view事件傳遞的流程,當然還沒有包括點擊,長按等事件。下面對全文做個總結: (1) 事件從 Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的 View(ViewGroup)開始一直往下(子 View)傳遞。子 View 可以通過 onTouchEvent()對事件進行處理。

    (2) 事件由父 View(ViewGroup)傳遞給子 View,ViewGroup 可以通過 onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。

    (3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子 View 沒有消費事件,事件會反向往上傳遞,這時父 View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最后會到 Activity 的 onTouchEvent()函數。

    (4) 如果 View 沒有對 ACTION_DOWN 進行消費,之后的其他事件不會傳遞過來。

    (5) OnTouchListener 優先于 onTouchEvent()對事件進行消費。

    源代碼:鏈接:http://pan.baidu.com/s/1sjsUmsP 密碼:v6lp

    </blockquote> 來自:http://my.oschina.net/crazy261/blog/483836

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!