標籤:
當Android系統捕獲到使用者的各種輸入事件後,如何準確的傳遞給真正的需要這個事件的控制項?Android提供了一整套完善的事件傳遞、處理機制,來協助開發人員完成準確的事件分配與處理,這裡我就不分析源碼了,簡單點,圖形化分發過程,便於理解。
當我們點擊一個按鈕時,通常會產生兩個或者三個事件---按下、滑動(可能無)、抬起。Android為觸摸事件封裝了一個類----MotionEvent,其中假如我們重寫一個view的onTouchEvent事件中的參數就是一個MotionEvent。由於Android的View結構是樹形結構,也就是說,View可以放在ViewGroup裡面,通過不同的組合來實現不同的樣式。View可以放在一個ViewGroip,而這個ViewGroup又放在另一個ViewGroup裡面,甚至還有可能繼續嵌套。可能同一個事件,子View和ViewGroup都有可能想要進行處理,因此怎麼樣去“分發“和“攔截”的問題就產生了。
假設有這麼一個View,一個ViewGroupA,裡面嵌套了另一個ViewGroupB,而ViewGroupB裡面有一個view。整體的布局結構如下:
布局檔案如下:
<RelativeLayout 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" tools:context="com.example.viewdispatch.MainActivity" > <com.example.views.MyViewGroupA android:layout_width="260dp" android:layout_height="360dp" android:background="#f42c61" > <com.example.views.MyViewGroupB android:layout_width="200dp" android:layout_height="300dp" android:background="#042c61" > <com.example.views.MyView android:layout_width="160dp" android:layout_height="260dp" android:background="#cccccc" /> </com.example.views.MyViewGroupB> </com.example.views.MyViewGroupA></RelativeLayout>
整體的Activity包含3個自訂的View,項目結構是MyView、MyViewGroupB、MyViewGroupA。所以整體Touch事件的主角是View和ViewGroup,而與View相關的Touch事件有2個dispatchTouchEvent和onTouchEvent;與ViewGroup相關的Touch事件有3個dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。所以我們在定義好View和ViewGroup後,讓其分別是實現這些方法。
代碼如下:
MyView.java
import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;public class MyView extends View {private String Tag = "MyView";public MyView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public MyView(Context context, AttributeSet attrs) {super(context, attrs);Log.d(Tag, "----->MyView");}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(Tag, "----->onTouchEvent");return super.onTouchEvent(event);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.e(Tag, "----->dispatchTouchEvent");return super.dispatchTouchEvent(event);}}MyViewGroupA.java
import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.ViewGroup;import android.widget.LinearLayout;public class MyViewGroupA extends LinearLayout {private String Tag = "MyViewGroupA";public MyViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// TODO 自動產生的建構函式存根}public MyViewGroupA(Context context, AttributeSet attrs) {super(context, attrs);Log.d(Tag, "----->MyViewGroupA");}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e(Tag, "----->dispatchTouchEvent");return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e(Tag, "----->onInterceptTouchEvent");return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(Tag, "----->onTouchEvent");return super.onTouchEvent(event);}}MyViewGroupB和A一樣,就不貼了。
其中上面事件的傳遞的返回值是這樣的,如果返回true,表示該View(ViewGroup)攔截了,不繼續往下分發事件了,如果返回false,表示不攔截,繼續往下分發。預設的調用父方法super.xxx是表示不攔截的意思。
下面就開始來驗證吧。
運行我們的項目,先看看我們3個view的載入情況吧:
可以看出是由外到內載入的。
好了,開始touch事件吧,現在我們先點擊最外層粉紅的ViewGroupA,觀察Log輸出:
然後點擊藍色地區的ViewGroupB,再觀察Log輸出:
最後點擊MyView觀察輸出
由此,我們可以大致的繪製出如所示的這樣的一個圖解流程。
現在假如我們修改MyViewGroupA中的onInterceptTouchEvent()事件,將其返回值改為True,點擊任意一層view,現在我們再看log輸出:
可以看出只有A的3個事件進行了處理,由此事件被A攔截了。流程圖如下所示:
同理,將A的返回值改回來,我們修改MyViewGroupB的onInterceptTouchEvent()方法,也返回true,然後點擊MyView或者MyViewGroupB,試試。Log輸出如下:
也就是事件只是分發到了MyViewGroupB,沒有到MyView,整個流程圖如下所示:
最後,假如底層的MyView想處理呢,相應的我們只用修改下onTouchEvent()的返回值就好,將其改為返回true.然後點擊MyView看看log輸出。
流程圖是這樣的:
好了,文章到此結束,先簡單的對分發和攔截有個大致的瞭解,但事件分發機制還沒完全分析完,等待下一篇再來分析。
最後,如果對文章有什麼疑惑,歡迎指出,共同進步!
Android圖解淺析事件攔截機制