前言
- Android事件分發機制是每個Android開發者必須瞭解的基礎知識
- 網上有大量關於Android事件分發機制的文章,但存在一些問題:內容不全、思路不清晰、無源碼分析、簡單問題複雜化等等
-
今天,我將全面總結Android的事件分發機制,我能保證這是市面上的最全面、最清晰、最易懂的
- 本文秉着“結論先行、詳細分析在後”的原則,即先讓大家感性認識,再通過理性分析從而理解問題;
- 所以,請各位讀者先記住結論,再往下繼續看分析;
-
文章較長,閱讀需要較長時間,建議收藏等充足時間再進行閱讀
目錄
1. 基礎認知
1.1 事件分發的對象是誰?
答:事件
-
當用戶觸摸屏幕時(View或ViewGroup派生的控件),將產生點擊事件(Touch事件)。
Touch事件相關細節(發生觸摸的位置、時間、歷史記錄、手勢動作等)被封裝成MotionEvent對象
-
主要發生的Touch事件有如下四種:
- MotionEvent.ACTION_DOWN:按下View(所有事件的開始)
- MotionEvent.ACTION_MOVE:滑動View
- MotionEvent.ACTION_CANCEL:非人爲原因結束本次事件
- MotionEvent.ACTION_UP:擡起View(與DOWN對應)
-
事件列:從手指接觸屏幕至手指離開屏幕,這個過程產生的一系列事件
任何事件列都是以DOWN事件開始,UP事件結束,中間有無數的MOVE事件,如下圖:
即當一個MotionEvent 產生後,系統需要把這個事件傳遞給一個具體的 View 去處理,
1.2 事件分發的本質
答:將點擊事件(MotionEvent)向某個View進行傳遞並最終得到處理
即當一個點擊事件發生後,系統需要將這個事件傳遞給一個具體的View去處理。這個事件傳遞的過程就是分發過程。
1.3 事件在哪些對象之間進行傳遞?
答:Activity、ViewGroup、View
一個點擊事件產生後,傳遞順序是:Activity(Window) -> ViewGroup -> View
-
Android的UI界面是由Activity、ViewGroup、View及其派生類組合而成的
-
View是所有UI組件的基類
一般Button、ImageView、TextView等控件都是繼承父類View
-
ViewGroup是容納UI組件的容器,即一組View的集合(包含很多子View和子VewGroup),
- 其本身也是從View派生的,即ViewGroup是View的子類
- 是Android所有佈局的父類或間接父類:項目用到的佈局(LinearLayout、RelativeLayout等),都繼承自ViewGroup,即屬於ViewGroup子類。
- 與普通View的區別:ViewGroup實際上也是一個View,只不過比起View,它多了可以包含子View和定義佈局參數的功能。
1.4 事件分發過程由哪些方法協作完成?
答:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()
下文會對這3個方法進行詳細介紹
1.5 總結
- Android事件分發機制的本質是要解決:點擊事件由哪個對象發出,經過哪些對象,最終達到哪個對象並最終得到處理。
這裏的對象是指Activity、ViewGroup、View
- Android中事件分發順序:Activity(Window) -> ViewGroup -> View
- 事件分發過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個方法協助完成
經過上述3個問題,相信大家已經對Android的事件分發有了感性的認知,接下來,我將詳細介紹Android事件分發機制。
2. 事件分發機制方法&流程介紹
- 事件分發過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個方法協助完成,如下圖:
- Android事件分發流程如下:(必須熟記)
Android事件分發順序:Activity(Window) -> ViewGroup -> View
其中:
- super:調用父類方法
- true:消費事件,即事件不繼續往下傳遞
- false:不消費事件,事件也不繼續往下傳遞 / 交由給父控件onTouchEvent()處理
接下來,我將詳細介紹這3個方法及相關流程。
2.1 dispatchTouchEvent()
屬性 | 介紹 |
---|---|
使用對象 | Activity、ViewGroup、View |
作用 | 分發點擊事件 |
調用時刻 | 當點擊事件能夠傳遞給當前View時,該方法就會被調用 |
返回結果 | 是否消費當前事件,詳細情況如下: |
1. 默認情況:根據當前對象的不同而返回方法不同
對象 | 返回方法 | 備註 |
---|---|---|
Activity | super.dispatchTouchEvent() | 即調用父類ViewGroup的dispatchTouchEvent() |
ViewGroup | onIntercepTouchEvent() | 即調用自身的onIntercepTouchEvent() |
View | onTouchEvent() | 即調用自身的onTouchEvent() |
2. 返回true
- 消費事件
- 事件不會往下傳遞
- 後續事件(Move、Up)會繼續分發到該View
- 流程圖如下:
3. 返回false
- 不消費事件
- 事件不會往下傳遞
-
將事件回傳給父控件的onTouchEvent()處理
Activity例外:返回false=消費事件
-
後續事件(Move、Up)會繼續分發到該View(與onTouchEvent()區別)
- 流程圖如下:
2.2 onTouchEvent()
屬性 | 介紹 |
---|---|
使用對象 | Activity、ViewGroup、View |
作用 | 處理點擊事件 |
調用時刻 | 在dispatchTouchEvent()內部調用 |
返回結果 | 是否消費(處理)當前事件,詳細情況如下: |
與dispatchTouchEvent()類似
1. 返回true
- 自己處理(消費)該事情
- 事件停止傳遞
- 該事件序列的後續事件(Move、Up)讓其處理;
- 流程圖如下:
2. 返回false(同默認實現:調用父類onTouchEvent())
- 不處理(消費)該事件
- 事件往上傳遞給父控件的onTouchEvent()處理
- 當前View不再接受此事件列的其他事件(Move、Up);
- 流程圖如下:
2.3 onInterceptTouchEvent()
屬性 | 介紹 |
---|---|
使用對象 | ViewGroup(注:Activity、View都沒該方法) |
作用 | 攔截事件,即自己處理該事件 |
調用時刻 | 在ViewGroup的dispatchTouchEvent()內部調用 |
返回結果 | 是否攔截當前事件,詳細情況如下: |
- 流程圖如下:
2.4 三者關係
下面將用一段僞代碼來闡述上述三個方法的關係和點擊事件傳遞規則
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
2.5 總結
- 對於事件分發的3個方法,你應該清楚瞭解
- 接下來,我將開始介紹Android事件分發的常見流程
3. 事件分發場景介紹
下面我將利用例子來說明常見的點擊事件傳遞情況
3.1 背景描述
我們將要討論的佈局層次如下:
- 最外層:Activiy A,包含兩個子View:ViewGroup B、View C
- 中間層:ViewGroup B,包含一個子View:View C
- 最內層:View C
假設用戶首先觸摸到屏幕上View C上的某個點(如圖中黃色區域),那麼Action_DOWN事件就在該點產生,然後用戶移動手指並最後離開屏幕。
3.2 一般的事件傳遞情況
一般的事件傳遞場景有:
- 默認情況
- 處理事件
- 攔截DOWN事件
- 攔截後續事件(MOVE、UP)
3.2.1 默認情況
- 即不對控件裏的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())進行重寫或更改返回值
- 那麼調用的是這3個方法的默認實現:調用父類的方法
- 事件傳遞情況:(如圖下所示)
- 從Activity A—->ViewGroup B—>View C,從上往下調用dispatchTouchEvent()
- 再由View C—>ViewGroup B —>Activity A,從下往上調用onTouchEvent()
注:雖然ViewGroup B的onInterceptTouchEvent方法對DOWN事件返回了false,後續的事件(MOVE、UP)依然會傳遞給它的onInterceptTouchEvent()
這一點與onTouchEvent的行爲是不一樣的。
3.2.2 處理事件
假設View C希望處理這個點擊事件,即C被設置成可點擊的(Clickable)或者覆寫了C的onTouchEvent方法返回true。
最常見的:設置Button按鈕來響應點擊事件
事件傳遞情況:(如下圖)
- DOWN事件被傳遞給C的onTouchEvent方法,該方法返回true,表示處理這個事件
- 因爲C正在處理這個事件,那麼DOWN事件將不再往上傳遞給B和A的onTouchEvent();
- 該事件列的其他事件(Move、Up)也將傳遞給C的onTouchEvent()
3.2.3 攔截DOWN事件
假設ViewGroup B希望處理這個點擊事件,即B覆寫了onInterceptTouchEvent()返回true、onTouchEvent()返回true。
事件傳遞情況:(如下圖)
- DOWN事件被傳遞給B的onInterceptTouchEvent()方法,該方法返回true,表示攔截這個事件,即自己處理這個事件(不再往下傳遞)
-
調用onTouchEvent()處理事件(DOWN事件將不再往上傳遞給A的onTouchEvent())
-
該事件列的其他事件(Move、Up)將直接傳遞給B的onTouchEvent()
該事件列的其他事件(Move、Up)將不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會被調用了。
3.2.4 攔截DOWN的後續事件
假設ViewGroup B沒有攔截DOWN事件(還是View C來處理DOWN事件),但它攔截了接下來的MOVE事件。
- DOWN事件傳遞到C的onTouchEvent方法,返回了true。
- 在後續到來的MOVE事件,B的onInterceptTouchEvent方法返回true攔截該MOVE事件,但該事件並沒有傳遞給B;這個MOVE事件將會被系統變成一個CANCEL事件傳遞給C的onTouchEvent方法
-
後續又來了一個MOVE事件,該MOVE事件纔會直接傳遞給B的onTouchEvent()
- 後續事件將直接傳遞給B的onTouchEvent()處理
- 後續事件將不會再傳遞給B的onInterceptTouchEvent方法,該方法一旦返回一次true,就再也不會被調用了。
-
C再也不會收到該事件列產生的後續事件。
特別注意:
- 如果ViewGroup A 攔截了一個半路的事件(如MOVE),這個事件將會被系統變成一個CANCEL事件並傳遞給之前處理該事件的子View;
- 該事件不會再傳遞給ViewGroup A的onTouchEvent()
- 只有再到來的事件纔會傳遞到ViewGroup A的onTouchEvent()
3.3 總結
- 對於Android的事件分發機制,你應該已經非常清楚了
- 如果你只是希望瞭解Android事件分發機制而不想深入瞭解,那麼你可以離開這篇文章了
- 對於程序猿來說,知其然還需要知其所以然,接下來,我將通過源碼分析來深入瞭解Android事件分發機制
4. Android事件分發機制源碼分析
- Android中事件分發順序:Activity(Window) -> ViewGroup -> View,再次貼出下圖:
其中:
- super:調用父類方法
- true:消費事件,即事件不繼續往下傳遞
- false:不消費事件,事件繼續往下傳遞 / 交由給父控件onTouchEvent()處理
所以,要想充分理解Android分發機制,本質上是要理解:
- Activity對點擊事件的分發機制
- ViewGroup對點擊事件的分發機制
- View對點擊事件的分發機制
接下來,我將通過源碼分析詳細介紹Activity、View和ViewGroup的事件分發機制
4.1 Activity的事件分發機制
4.1.1 源碼分析
- 當一個點擊事件發生時,事件最先傳到Activity的dispatchTouchEvent()進行事件分發
具體是由Activity的Window來完成
- 我們來看下Activity的dispatchTouchEvent()的源碼
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
關注點1
一般事件列開始都是DOWN(按下按鈕),所以這裏返回true,執行onUserInteraction()
關注點2
先來看下onUserInteraction()源碼
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 該方法爲空方法
- 從註釋得知:當此activity在棧頂時,觸屏點擊按home,back,menu鍵等都會觸發此方法
- 所以onUserInteraction()主要用於屏保
關注點3
- Window類是抽象類,且PhoneWindow是Window類的唯一實現類
- superDispatchTouchEvent(ev)是抽象方法,返回的是一個Window對象
- 通過PhoneWindow類中看一下superDispatchTouchEvent()的作用
- 1
- 2
- 3
- 4
- 5
- 6
- 接下來我們看mDecor.superDispatchTouchEvent(event):
- 1
- 2
- 3
- 4
- 5
- 6
- 7
所以:
- 執行
getWindow().superDispatchTouchEvent(ev)
實際上是執行了ViewGroup.dispatchTouchEvent(event)
- 再回到最初的代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
由於一般事件列開始都是DOWN,所以這裏返回true,基本上都會進入getWindow().superDispatchTouchEvent(ev)
的判斷
- 所以,執行
Activity.dispatchTouchEvent(ev)
實際上是執行了ViewGroup.dispatchTouchEvent(event)
- 這樣事件就從 Activity 傳遞到了 ViewGroup
4.1.2 彙總
當一個點擊事件發生時,調用順序如下
- 事件最先傳到Activity的dispatchTouchEvent()進行事件分發
- 調用Window類實現類PhoneWindow的superDispatchTouchEvent()
- 調用DecorView的superDispatchTouchEvent()
- 最終調用DecorView父類的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent()
4.1.3 結論
- 當一個點擊事件發生時,事件最先傳到Activity的dispatchTouchEvent()進行事件分發,最終是調用了ViewGroup的dispatchTouchEvent()方法
如果ViewGroup的dispatchTouchEvent()返回true就不執行Activity的onTouchEvent()方法;如果返回false,就執行。
- 這樣事件就從 Activity 傳遞到了 ViewGroup
4.1.4 疑問
那麼,ViewGroup的dispatchTouchEvent()什麼時候返回true,什麼時候返回false?
答:請繼續往下看 ViewGroup事件的分發機制
4.2 ViewGroup事件的分發機制
在講解ViewGroup事件的分發機制之前我們先來看個Demo4.2.1 Demo講解
佈局如下:
結果測試:
只點擊Button
再點擊空白處
從上面的測試結果發現:
- 當點擊Button時,執行Button的onClick(),但ViewGroupLayout註冊的onTouch()不會執行
- 只有點擊空白區域時纔會執行ViewGroupLayout的onTouch();
- 結論:Button的onClick()將事件消費掉了,因此事件不會再繼續向下傳遞。
接下來,我們開始進行ViewGroup事件分發的源碼分析
4.2.2 源碼分析
ViewGroup的dispatchTouchEvent()源碼分析
- 詳情請看註釋
- Android 5.0後ViewGroup的dispatchTouchEvent()的源碼發生了變化(更加複雜),但原理相同;
- 本文爲了讓讀者更好理解dispatchTouchEvent()源碼分析,所以採用Android 5.0前的版本
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
關注點1(onInterceptTouchEvent()源碼分析)
ViewGroup每次在做分發時,需要調用onInterceptTouchEvent()是否攔截事件;源碼分析如下:
- 1
- 2
- 3
- 返回false =不攔截(默認),允許事件繼續往下傳遞(向子View傳遞);
因爲子View也需要該事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的 = 不會攔截
- 返回true = 攔截(手動設置),即自己處理該事件(執行自己的onTouchEvent()),事件不會繼續往下傳遞
關注點2
當點擊了某個控件:- 調用該控件所在佈局(ViewGroup)的dispatchTouchEvent()
- 在佈局的dispatchTouchEvent()中找到被點擊的相應控件
- 再去調用該控件的dispatchTouchEvent()
- 實現了點擊事件從ViewGroup到View的傳遞
- 此處是關於View.dispatchTouchEvent()的分析,詳情請看下面的View事件分發機制。
- 實現了點擊事件從ViewGroup到View的傳遞
結論
- Android事件分發是先傳遞到ViewGroup,再由ViewGroup傳遞到View
- 在ViewGroup中通過onInterceptTouchEvent()對事件傳遞進行攔截
- onInterceptTouchEvent方法返回true代表攔截事件,即不允許事件繼續向子View傳遞;
- 返回false代表不攔截事件,即允許事件繼續向子View傳遞;(默認返回false)
- 子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件。
- onInterceptTouchEvent方法返回true代表攔截事件,即不允許事件繼續向子View傳遞;
4.3 View事件的分發機制
View中dispatchTouchEvent()的源碼分析- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 只有以下三個條件都爲真,dispatchTouchEvent()才返回true;否則執行onTouchEvent(event)方法
- 1
- 2
- 3
- 下面,我們來看看下這三個判斷條件:
第一個條件:mOnTouchListener!= null
- 1
- 2
- 3
- 4
- 5
- 6
- 7
第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED
- 該條件是判斷當前點擊的控件是否enable
- 由於很多View默認是enable的,因此該條件恆定爲true
第三個條件:mOnTouchListener.onTouch(this, event)
- 回調控件註冊Touch事件時的onTouch方法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 如果在onTouch方法返回true,就會讓上述三個條件全部成立,從而整個方法直接返回true。
- 如果在onTouch方法裏返回false,就會去執行onTouchEvent(event)方法。
接下來,我們繼續看:onTouchEvent(event)的源碼分析
- 詳情請看註釋
- Android 5.0後View的onTouchEvent()的源碼發生了變化(更加複雜),但原理相同;
- 本文爲了讓讀者更好理解onTouchEvent()源碼分析,所以採用Android 5.0前的版本
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
關注點1:
performClick()的源碼分析
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 只要mOnClickListener不爲null,就會去調用onClick方法;
- 那麼,mOnClickListener又是在哪裏賦值的呢?請繼續看:
- 1
- 2
- 3
- 4
- 5
- 6
- 當我們通過調用setOnClickListener方法來給控件註冊一個點擊事件時,就會給mOnClickListener賦值(不爲空),即會回調onClick()。
結論
- onTouch()的執行高於onClick()
- 每當控件被點擊時:
- 如果在回調onTouch()裏返回false,就會讓dispatchTouchEvent方法返回false,那麼就會執行onTouchEvent();如果回調了setOnClickListener()來給控件註冊點擊事件的話,最後會在performClick()方法裏回調onClick()。
onTouch()返回false(該事件沒被onTouch()消費掉) = dispatchTouchEvent()返回false(繼續向下傳遞) = 執行onTouchEvent() = 執行OnClick()
- 如果在回調onTouch()裏返回true,就會讓dispatchTouchEvent方法返回true,那麼將不會執行onTouchEvent(),即onClick()也不會執行;
onTouch()返回true(該事件被onTouch()消費掉) = dispatchTouchEvent()返回true(不會再繼續向下傳遞) = 不會執行onTouchEvent() = 不會執行OnClick()
下面我將用Demo驗證上述的結論
Demo論證
1. Demo1:在回調onTouch()裏返回true
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
點擊Button,測試結果如下:
2. Demo2:在回調onTouch()裏返回false
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
點擊Button,測試結果如下:
總結:onTouch()返回true就認爲該事件被onTouch()消費掉,因而不會再繼續向下傳遞,即不會執行OnClick()。
如果你看到此處,那麼恭喜你,你已經能非常熟悉掌握Android的事件分發機制了(Activity、ViewGroup、View的事件分發機制)
5. 思考點
5.1 onTouch()和onTouchEvent()的區別
- 這兩個方法都是在View的dispatchTouchEvent中調用,但onTouch優先於onTouchEvent執行。
-
如果在onTouch方法中返回true將事件消費掉,onTouchEvent()將不會再執行。
-
特別注意:請看下面代碼
- 1
- 2
- 3
- 4
- 5
- 6
- 因此如果你有一個控件是非enable的,那麼給它註冊onTouch事件將永遠得不到執行。對於這一類控件,如果我們想要監聽它的touch事件,就必須通過在該控件中重寫onTouchEvent方法來實現。
5.2 Touch事件的後續事件(MOVE、UP)層級傳遞
- 如果給控件註冊了Touch事件,每次點擊都會觸發一系列action事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP等)
- 當dispatchTouchEvent在進行事件分發的時候,只有前一個事件(如ACTION_DOWN)返回true,纔會收到後一個事件(ACTION_MOVE和ACTION_UP)
即如果在執行ACTION_DOWN時返回false,後面一系列的ACTION_MOVE和ACTION_UP事件都不會執行
從上面對事件分發機制分析知:
- dispatchTouchEvent()和 onTouchEvent()消費事件、終結事件傳遞(返回true)
- 而onInterceptTouchEvent 並不能消費事件,它相當於是一個分叉口起到分流導流的作用,對後續的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用
請記住:接收了ACTION_DOWN事件的函數不一定能收到後續事件(ACTION_MOVE、ACTION_UP)
這裏給出ACTION_MOVE和ACTION_UP事件的傳遞結論:
- 如果在某個對象(Activity、ViewGroup、View)的dispatchTouchEvent()消費事件(返回true),那麼收到ACTION_DOWN的函數也能收到ACTION_MOVE和ACTION_UP
黑線:ACTION_DOWN事件傳遞方向
紅線:ACTION_MOVE和ACTION_UP事件傳遞方向
- 如果在某個對象(Activity、ViewGroup、View)的onTouchEvent()消費事件(返回true),那麼ACTION_MOVE和ACTION_UP的事件從上往下傳到這個View後就不再往下傳遞了,而直接傳給自己的onTouchEvent()並結束本次事件傳遞過程。
黑線:ACTION_DOWN事件傳遞方向
紅線:ACTION_MOVE和ACTION_UP事件傳遞方向