編輯:Android資訊
事件分發是Android中非常重要的機制,是用戶與界面交互的基礎。這篇文章將通過示例打印出的Log,繪制出事件分發的流程圖,讓大家更容易的去理解Android的事件分發機制。
Android中與事件分發相關的方法主要包括dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三個方法,而事件分發一般會經過三種容器,分別為Activity、ViewGroup、View。下表對這三種容器分別擁有的事件分發相關方法進行了整理。
這篇文章中我們只考慮4種觸摸事件: ACTION_DOWN、ACTION_UP、ACTION_MOVE、ACTION_CANAL。 事件序列:一個事件序列是指從手指觸摸屏幕開始,到手指離開屏幕結束,這個過程中產生的一系列事件。一個事件序列以ACTION_DOWN事件開始,中間可能經過若干個MOVE,以ACTION_UP事件結束。 接下來我們將使用之前的文章自定義View——彈性滑動中例子來作為本文的示例,簡單增加一些代碼即可,修改之後的代碼請點擊查看。
我們可以從示例代碼的xml中看出,圖片都是可點擊的。
<?xml version="1.0" encoding="utf-8"?> <com.idtk.customscroll.ParentView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" tools:context="com.idtk.customscroll.MainActivity" > <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/zhiqinchun" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/hanzhan" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/shengui" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/dayu" android:clickable="true"/> </com.idtk.customscroll.ParentView>
我們現在來點擊一下,查看下打印出的日志。
根據打印出的log來繪制一張事件傳遞的流程圖
現在來理一下事件序列的流程:
這裡使用工作中的情況來模擬一下:老板(Activity)、項目經理(ViewGroup)、軟件工程師(View)
通過上面的傳遞過程,我們可以得出一些結論:
我們現在修改示例代碼的xml部分,android:clickable="true"
全部修改為android:clickable="false"
<?xml version="1.0" encoding="utf-8"?> <com.idtk.customscroll.ParentView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" tools:context="com.idtk.customscroll.MainActivity" > <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/zhiqinchun" android:clickable="false"/> <com.idtk.customscroll.ChildView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/hanzhan" android:clickable="false"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/shengui" android:clickable="false"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/dayu" android:clickable="false"/> </com.idtk.customscroll.ParentView>
這時再點擊一下,查看新打印出的日志
現在根據log中顯示的邏輯,分別繪制ACTION_DOWN事件與ACTION_UP事件傳遞的流程圖
我們來整理下這個事件序列的流程:
這裡使用工作中的情況來模擬:依舊是老板(Activity)、項目經理(ViewGroup)、軟件工程師(View) 從老板交任務給項目經理,項目經理交任務給工程師,這一段流程和之前的例子相同。不同之處是軟件工程師沒有完成這個任務(View#onTouchEvent返回false),告訴項目經理我沒有完成,然後項目經理自己進行了嘗試,同樣沒有完成(ViewGroup#onTouchEvent返回false),項目經理告訴了老板,我沒有完成,然後老板自己試了下也沒有完成這個任務(Activity#onTouchEvent返回false),但之後的也有項目的二期、三期,不過老板知道你們完成不了,所以都是他自己進行嘗試,不過很慘都沒完成。(這段有點與正常情況不同,不過只是打個比方)
通過結合上面兩個例子,可以得出一些結論:
事件分發中攔截的情況,這裡我把它分為2種,一種是在ACTION_DOWN事件時,就進行攔截的;另一種是在ACTION_DOWN之後的事件序列中,對事件進行了攔截。
為了達到在ViewGroup中,一開始就攔截觸摸事件的效果,我們需要進行修改,在ParentView#onInterceptTouchEvent方法的最後部分,我注釋掉的intercept=true;
進行恢復,然後為activity_main.xml中的ParentView增加android:clickable="true"
屬性。
<?xml version="1.0" encoding="utf-8"?> <com.idtk.customscroll.ParentView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" tools:context="com.idtk.customscroll.MainActivity" > <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/zhiqinchun" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/hanzhan" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/shengui" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/dayu" android:clickable="true"/> </com.idtk.customscroll.ParentView>
我們現在來看下攔截情況下的事件流程圖
這裡大部分和之前的例子相同,主要的區別是在於ViewGroup#onInterceptTouchEvent方法中,對傳遞的事件進行了攔截,返回true,ACTION_DOWN事件就傳遞到了ViewGroup#onTouchEvent中進行處理,ACTION_DOWN事件之後的傳遞就與之前的例子相同了。另一點重要的區別是,在ViewGroup攔截下事件之後,此事件序列的其余事件,在進入ViewGroup#dispatchTouchEvent方法之後,不在需要進行是否攔截事件的判斷,而是直接進入了onTouchEvent方法之中。
使用工作中的情況來模擬:老板(Activity)、項目經理(ViewGroup)、軟件工程師(View) 老板吧任務交給項目經理,項目經理認為這個項目比較難,所以決定自己處理(ViewGroup#onInterceptTouchEvent,return true),項目經理比較厲害他把任務完成了(ViewGroup#onTouchEvent,return true),然後他告訴老板他完成了(return true,ViewGroup#dispatchTouchEvent→Activity#dispatchTouchEvent)。之後老板依舊會把任務交給項目經理,項目經理知道這個任務難度,所以不假思索(也就是這個事件序列中的其余事件沒有經過ViewGroup#onInterceptTouchEvent)的自己來做。
通過上面的例子,可以得出一些結論:
這裡把使用的示例恢復到初始狀態,然後把我在ParentView#onInterceptTouchEvent方法,switch內的兩個注釋掉的intercept = true;
代碼進行恢復,最後部分intercept = true;
再次注釋掉。
<?xml version="1.0" encoding="utf-8"?> <com.idtk.customscroll.ParentView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" tools:context="com.idtk.customscroll.MainActivity" > <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/zhiqinchun" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/hanzhan" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/shengui" android:clickable="true"/> <com.idtk.customscroll.ChildView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/dayu" android:clickable="true"/> </com.idtk.customscroll.ParentView>
重新運行之後,滑動一個圖片,來看看Log
這裡分成兩張圖片,是因為中間有很多ACTION_MOVE,為了方便觀察,所以只截取了Log的首尾部分。 這裡的關鍵部分,就是紅框中的ACTION_CANCEL,可以看到ACTION_DOWN事件的傳遞時onInterceptTouchEvent並沒有攔截,返回false,在其後的事件ACTION_MOVE再次進入onInterceptTouchEvent時,ViewGroup對事件進行了攔截,這樣將會對View傳遞一個ACTION_CANCEL事件,之後的ACTION_MOVE事件就不再傳遞給View了。
使用工作中的情況來模擬:老板(Activity)、項目經理(ViewGroup)、軟件工程師(View) 這裡的情況就是,一期的任務和第一個例子一樣的情況一樣,由軟件工程師完成,不過忽然項目經理覺得二期的任務有點難,然後決定自己完成。這時就給工程師說,這個項目的後續任務,不要你來完成了(ACTION_CANCEL)。
從這裡也可以得出一個結論:
本文通過示例打印出的各種Log對Android的事件分發機制進行,得出如下結論。
AValidations 是 一個免費的、開源的、簡易的、遵循Apache Licence 2.0開源協議發布,超級容易擴展的android表單驗證的小框架 使
如果你正好擁有全球第一支運行 Ubuntu 的手機並且希望將 BQ Aquaris E4.5 自帶的 Ubuntu 系統換成 Android,那這篇文章能幫你點小
模式的定義 適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。 使用場景 用電源接口做例子,筆
一、ActionBar概述 ActionBar是androiD3.0以後新增的組件,主要用於標示應用程序以及用戶所處的位置並提供相關操作以及全局的導航功能。下面我