完全理解android事件分發機制
前言
之前筆者其實已經寫過事件分發機制的文章:快速理解android事件傳遞攔截機制概念
但是,現在看來其實更像是一篇知識概括,多處可能未講清楚,於是打算重寫事件分發,用一篇文章大致講清楚。
首先,形式上筆者最先思考的是使用原始碼,此者能從原理上講解分發機制,比起侃侃而談好得多。但是大量的原始碼往往會讓新手產生畏懼難以理解,於是筆者最終還是打算主要使用例項log輸出來讓讀者理解android事件分發。
重要函式
筆者此次主要提及最常用的幾個函式:
(其間區別看原始碼很容易理解,此處直接給上結果)
onClick():這個函式是是View提供給我們的OnClickListener這個介面中的函式,在這裡可以自定義對點選事件的處理邏輯。會在onTouchEvent()中進行呼叫。
onTouch():這個函式是View提供給我們的OnTouchListener這個介面中的函式,在這裡面可以自定義對觸控事件的處理邏輯。
onTouchEvent():這個函式是view內部的觸控事件的處理方式,其間包括獲取焦點,呼叫onClick()等等。
dispatchTouchEvent():這個是View的事件分發函式,在ViewGroup中進行重寫。在View中其間會呼叫onTouchEvent(),在ViewGroup中其間會呼叫onInterceptTouchEvent()和onTouchEvent()。
onInterceptTouchEvent():這個函式是事件攔截函式,是ViewGroup才有的函式。
重要函式執行順序
此處我們通過一個很簡單的例子進行說明,示例:
示例xml程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<com.example.double2.dispatchevent.LinearLayoutA
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_a"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_dark"
android:padding="30dp"
android:orientation="vertical"
>
<com.example.double2.dispatchevent.LinearLayoutB
android:id="@+id/ll_b"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:padding="30dp"
android:orientation="vertical"
>
<com.example.double2.dispatchevent.LinearLayoutC
android:id="@+id/v_c"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
/>
</com.example.double2.dispatchevent.LinearLayoutB>
</com.example.double2.dispatchevent.LinearLayoutA>
由外到裡主要是三個LinearLayout,分別為A、B、C。筆者分別在五個關鍵函式中加上了Log,最終點選一下,輸出的值如下:
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:28:58.136 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:28:58.203 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_C
如上,我們可以看到五個函式的大致執行順序如下:
- dispatchTouchEvent()
- onInterceptTouchEvent()
- onTouch()
- onTouchEvent()
- onClick()
好奇的讀者肯定會問,為什麼事件分發執行了兩次呢?
其實很簡單,因為的確有兩個分發的事件,一次是“手指按下”的事件,一次是“手指抬起”的事件。我們可以看到,只有在“手指抬起”的時候,才會觸發onClick()事件。
此處為了便於大家理解,也附上一張事件分發的圖:
控制事件分發
然而還有一個值得我們在意的事情,就是onTouch()以及onTouchEvent()只有在C中執行,而在B和A的就不執行了。
此處,我們就必須再講一點了。
dispatchTouchEvent()、onInterceptTouchEvent() 、onTouch()、onTouchEvent()這四個函式,返回值為false的時候,事件會繼續向下分發,一旦返回值為true,事件就不再向下分發。
而onClick()沒有返回值
根據這點我們可以知道,一定是C的onTouchEvent()中返回了true,我們將其更改後再看一下效果。
原來的程式碼為:
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("XJJ","onTouchEvent_C");
return super.onTouchEvent(event);
}
更改後:
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("XJJ","onTouchEvent_C");
return false;
}
效果:
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:24:06.250 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:24:06.360 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_B
這樣操作之後,我們可以發現,“手指按下”時,onTouch()以及onTouchEvent()事件就可以傳遞到B了。但是同時,我們也可以發現,當“手指抬起”時,C的onTouch()以及onTouchEvent()事件就不會執行了。
當然,如果需要onTouch()以及onTouchEvent()事件傳遞到A,那麼只需要將B的onTouchEvent()也返回false即可。(此處就不重複演示了)
那麼如果onTouchEvent()返回值設定為true了之後,是不是onClick()事件是不是就不會執行了呢?效果如下:
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:39:21.560 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:39:21.617 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_C
onClick()的確是不會執行了,如此我們也嘗試一下onTouch()返回值設定為true,效果如下:
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_C
01-12 01:40:41.101 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_C
01-12 01:40:41.173 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_C
如此,我們可知,當onTouch()返回值設定為true的時候,onTouchEvent()的確是不會執行了。
到這裡,我們其實只剩下對onInterceptTouchEvent() 的分析了,為何沒有dispatchTouchEvent()了呢?
因為dispatchTouchEvent()是事件分發的函式,對View而言,我們阻止它內部的事件分發是沒有什麼意義的,而我們要控制ViewGroup的事件分發則是通過onInterceptTouchEvent() 來執行的。
如此我們便假設一個應用場景,A包括B,B包括C,B為橫向滑動,C為豎向滑動,當橫向滑動的加速度大於豎向滑動的加速度的時候,我們僅僅讓B響應事件,而不把事件傳遞給C。
我們可以通過onInterceptTouchEvent() 來實現,僅僅只需將B的onInterceptTouchEvent()返回值設定為true即可,效果如下:
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:50:18.513 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_A
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onInterceptTouchEvent_A
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: dispatchTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouch_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onTouchEvent_B
01-12 01:50:18.621 2442-2442/com.example.double2.dispatchevent D/XJJ: onClick_B
原始碼
最後是附上demo的原始碼,大家可以自行下載理解。
http://download.csdn.net/detail/double2hao/9735367
相關文章
- Android 事件分發機制的理解Android事件
- Android事件分發機制簡單理解Android事件
- 10分鐘理解 Android View 事件分發機制AndroidView事件
- Android事件分發機制Android事件
- Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上)Android事件原始碼
- Android事件分發機制探究Android事件
- Android事件分發機制解析Android事件
- Android事件分發機制三:事件分發工作流程Android事件
- android事件分發機制詳解Android事件
- Android的MotionEvent事件分發機制Android事件
- 圖解 Android 事件分發機制圖解Android事件
- Android ViewGroup事件分發機制AndroidView事件
- Android View 的事件體系 -- 事件分發機制AndroidView事件
- Android 事件分發機制原始碼解析Android事件原始碼
- 淺談Android 事件分發機制(二)Android事件
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- 那些你曾不知道的觸控事件—Android分發機制完全解析事件Android
- View事件分發機制View事件
- 淺談Android中的事件分發機制Android事件
- 面試:講講 Android 的事件分發機制面試Android事件
- 【Android原始碼】View的事件分發機制Android原始碼View事件
- Android ViewGroup 事件分發機制詳解AndroidView事件
- 《Android開發藝術探索》——View事件分發機制AndroidView事件
- Android事件分發機制,你瞭解過嗎?Android事件
- Android 事件分發機制原始碼解析-view層Android事件原始碼View
- 【Android基礎】講講Android的事件分發機制Android事件
- View事件分發機制分析View事件
- Android 事件分發完全解析之事件從何而來Android事件
- 通俗理解Android中View的事件分發機制及滑動衝突處理AndroidView事件
- Android事件分發機制五:面試官你坐啊Android事件面試
- Android自定義View之事件分發機制總結AndroidView事件
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- Android 事件分發機制原始碼詳解-最新 APIAndroid事件原始碼API
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- Android事件分發機制之原始碼完美解析(上)Android事件原始碼
- Android事件分發機制、滑動衝突解決Android事件
- 【漫畫技術】揭祕Android事件分發機制Android事件
- 事件分發機制(一):解惑篇事件