Android View 事件傳遞機制
View事件傳遞 touch事件分發
學習Android一年有余,今天開始以自己的理解去介紹一下Android開發常用到的基礎技術。第一個介紹的是View 的事件傳遞機制,建議首先去看一下[codeKK中關于View事件傳遞介紹的文章][1],我這里只是對這個的補充以及自己理解。有不正確的地方歡迎拍 磚?。
第一部分:知識儲備
- 在Android中所有的TouchEvent事件都封裝在MotionEvent對象中,而TouchEvent事件主要包括Touch的位置,時間,歷史記錄,滑動等等。
- 常見MotionEvent的類型包括:ACTION_DOWN,ACTION_MOVE,ACTION_CANCEL,ACTION_UP,ACTION_SCROLL等等。
- 我們關心的對事件的處理實質上就是 dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent這三個函數,分別代表事件傳遞,事件消費 和事件攔截的意思,這三個函數都是boolean類型的,如果返回值為true代表該事件被消耗。
(注明:以上的內容是從codeKK文章中部分摘取的)
</blockquote> </li> </ol>第二部分:實戰演練
以我的理解,要掌握整個事件的傳遞過程,必須得理清楚那三個事件處理函數,最好是自己寫寫代碼驗證一下。 以下我根據事件的消耗分四個部分:不攔截不消費,不攔截消費,攔截不消費,攔截消費。為了更好的理解,假設布局層次如下:
這個是在UI Automator中的截圖,布局很簡單,最外層為一個LinearLayout,然后嵌套了兩個LinearLayout。在這個里為了方便我簡稱為從最外到里分別為L0,L1,L2。其預覽圖如下:
布局文件如下:
<?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。預期的流程應該是:
當我們點擊L2即紅色區域程序運行的結果是:
可以看到和我們預期的一模一樣。
2.不攔截消費:
這里假定L1對事件不攔截消費,即L1.onInterceptTouchEvent位false,L1.onTouchEvent返回為true,說明L1對消費了事件,且沒有攔截事件。同樣我們的預期流程應該有兩種情況: 第一種:L2消費掉事件,即L2.onTouchEvent返回為true,其流程應該是:
當我們點擊L2即紅色區域程序運行的結果是:
第二種:L2沒有消費掉事件,即L2.onTouchEvent返回為fasle,其流程應該是:
當我們點擊L2即紅色區域程序運行的結果是:
到這里前兩種情況基本介紹完了,總結下,在這里最重要的一點要記住,如果子VIEW沒有消費掉ACTION_DOWN的事件,則后續的UP/MOVE事件將不會到來。
</blockquote>3.攔截不消費:
這里假定的對象還是L1,即L1對事件攔截但不消費,即L1.onInterceptTouchEvent位true,L1.onTouchEvent返回為false。從字面上可以L1攔截了事件,則L2是不會在接受到任何事件的。預期的流程應該是:
當我們點擊L2即紅色區域程序運行的結果是:
可以看到和我們流程圖是一樣的。
4.攔截消費:
這里假定的對還是L1,即L1對事件攔截且消費,即L1.onInterceptTouchEvent位true,L1.onTouchEvent返回為true。預期的流程應該是:
當我們點擊L2即紅色區域程序運行的結果是:
運行結果和流程圖一樣
第三部分:總結
以上就是就是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