以下內容轉自Android事件分發機制詳解:史上最全面、最易懂,感謝Carson_Ho的分享。
1.目錄
2 基礎知識
2.1 事件分發的對象是誰
答:點擊事件(Touch事件)
- 定義
當事件觸摸屏幕時(View
或ViewGroup
的派生控件),將產生點擊事件(Touch
事件)
Touch
事件的相關細節(發生觸發的位置、時間等)被封裝成MotionEvent
對象 - 事件類型
事件類型 | 具體動作 |
---|---|
MotionEvent.ACTION_DOWN | 按下View(所有事件的開始) |
MotionEvent.ACTION_UP | 擡起View(與DOWN對應) |
MotionEvent.ACTION_MOVE | 滑動View |
MotionEvent.ACTION_CANCEL | 結束事件(非人爲原因) |
- 特別說明
從手指接觸屏幕到手指離開屏幕,這個過程產生了一系列的事件
注:一般情況下,事件都是從DOWN
事件開始、UP
事件結束,中間有無數個MOVE
事件,如圖:
即一個點擊事件(MotionEvent
)產生後,系統需要把這個具體的事件傳遞給一個具體的View
去處理。
2.2 事件分發的本質
答:將點擊事件(MotionEvent
)傳遞到某個具體的View
和處理的整個過程
即事件傳遞的過程=分發過程
2.3 事件在哪些對象之間進行傳遞
答: Activity、ViewGroup、View Android
的UI 界面由Activity
、ViewGroup
、View
及其派生類組成
類型 | 簡介 | 備註 |
---|---|---|
Activity | 控制生命週期和處理事件 | * 統籌視圖的添加和顯示; * 通過其它回掉方法與 Window 、View 交互 |
View | 所有UI組建的基類 | 一般Button 、TextView 等控件都是繼承父類View |
ViewGroup | 一組View的集合 | * 其本身也是View 的子類;*是 Android 所有佈局的父類; *區別於普通 View :ViewGroup 實際上也是一個View ,只是可包含多個子View 和定義佈局參數的功能 |
2.4事件分發的順序
答:事件傳遞的順序:Activity
——>ViewGroup
——>View
即:一個點擊事件發生後,事件先傳到Activity
、再傳到ViewGroup
、最終再傳到View
2.5 事件分發過程由哪些方法協作完成
答:dispatchTouchEvent()
、onInterceptTouchEvent()
和onTouchEvent()
方法 | 作用 | 調用時刻 |
---|---|---|
dispatchTouchEvent() | 分發(傳遞)點擊事件 | 當點擊事件能夠傳遞給當前View時,該方法會被調用 |
onTouchEvent() | 處理點擊事件 | 在dispatchTouchEvent()內部調用 |
onInterceptTouchEvent() | 判斷是否攔截某個事件(只存在於ViewGroup、普通的View無該方法) | 在ViewGroup的dispatchTouchEvent()內部調用 |
3.事件分發機制 源碼分析
- 請謹記:Android事件分發流程:Activity->ViewGroup->View
即1個點擊事件後,事件先傳到Activity、再傳到ViewGroup、最終傳到View。
- 從上可知,要想充分理解Activity分發機制,本質上是要理解:
- Activity對點擊事件的分發機制
- ViewGroup對點擊事件的分發機制
- View對點擊事件的分發機制
- 下面,通過源碼,全面解析事件分發機制
按順序講解:Activity事件分發機制、Viewgroup事件分發機制、View事件分發機制。
3.1 Activity的事件分發機制
當一個點擊事件發生時,事件最先傳到Activity
的dispatchTouchEvent()
進行事件分發
3.1.1源碼分析
/**
* 源碼分析:Activity.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 一般事件列開始都是DOWN事件 = 按下事件,故此處基本是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
// ->>分析1
}
// ->>分析2
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 則Activity.dispatchTouchEvent()就返回true,則方法結束。即 :該點擊事件停止往下傳遞 & 事件傳遞過程結束
// 否則:繼續往下調用Activity.onTouchEvent
}
// ->>分析4
return onTouchEvent(ev);
}
/**
* 分析1:onUserInteraction()
* 作用:實現屏保功能
* 注:
* a. 該方法爲空方法
* b. 當此activity在棧頂時,觸屏點擊按home,back,menu鍵等都會觸發此方法
*/
public void onUserInteraction() {
}
// 回到最初的調用原處
/**
* 分析2:getWindow().superDispatchTouchEvent(ev)
* 說明:
* a. getWindow() = 獲取Window類的對象
* b. Window類是抽象類,其唯一實現類 = PhoneWindow類;即此處的Window類對象 = PhoneWindow類對象
* c. Window類的superDispatchTouchEvent() = 1個抽象方法,由子類PhoneWindow類實現
*/
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
// mDecor = 頂層View(DecorView)的實例對象
// ->> 分析3
}
/**
* 分析3:mDecor.superDispatchTouchEvent(event)
* 定義:屬於頂層View(DecorView)
* 說明:
* a. DecorView類是PhoneWindow類的一個內部類
* b. DecorView繼承自FrameLayout,是所有界面的父類
* c. FrameLayout是ViewGroup的子類,故DecorView的間接父類 = ViewGroup
*/
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
// 調用父類的方法 = ViewGroup的dispatchTouchEvent()
// 即 將事件傳遞到ViewGroup去處理,詳細請看ViewGroup的事件分發機制
}
// 回到最初的調用原處
/**
* 分析4:Activity.onTouchEvent()
* 定義:屬於頂層View(DecorView)
* 說明:
* a. DecorView類是PhoneWindow類的一個內部類
* b. DecorView繼承自FrameLayout,是所有界面的父類
* c. FrameLayout是ViewGroup的子類,故DecorView的間接父類 = ViewGroup
*/
public boolean onTouchEvent(MotionEvent event) {
// 當一個點擊事件未被Activity下任何一個View接收 / 處理時
// 應用場景:處理髮生在Window邊界外的觸摸事件
// ->> 分析5
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
// 即 只有在點擊事件在Window邊界外才會返回true,一般情況都返回false,分析完畢
}
/**
* 分析5:mWindow.shouldCloseOnTouch(this, event)
*/
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
// 主要是對於處理邊界外點擊事件的判斷:是否是DOWN事件,event的座標是否在邊界內等
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
// 返回true:說明事件在邊界外,即 消費事件
// 返回false:未消費(默認)
}
// 回到分析4調用原處
3.1.2 總結
- 當一個點擊事件發生時,從
Activity
的事件分發開始(Activity.dispatchTouchEvent
)
- 方法總結
那麼,ViewGroup
的dispatchTouchEvent()
什麼時候返回true
/false
?請繼續往下看ViewGroup
事件的分發機制。
3.2 ViewGroup事件的分發機制從
從上面Activity
事件分發機制可知,ViewGroup
事件分發機制從dispatchTouchEvent()
開始
3.2.1源碼分析
/**
* 源碼分析:ViewGroup.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
... // 僅貼出關鍵代碼
// 重點分析1:ViewGroup每次事件分發時,都需調用onInterceptTouchEvent()詢問是否攔截事件
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// 判斷值1:disallowIntercept = 是否禁用事件攔截的功能(默認是false),可通過調用requestDisallowInterceptTouchEvent()修改
// 判斷值2: !onInterceptTouchEvent(ev) = 對onInterceptTouchEvent()返回值取反
// a. 若在onInterceptTouchEvent()中返回false(即不攔截事件),就會讓第二個值爲true,從而進入到條件判斷的內部
// b. 若在onInterceptTouchEvent()中返回true(即攔截事件),就會讓第二個值爲false,從而跳出了這個條件判斷
// c. 關於onInterceptTouchEvent() ->>分析1
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
// 重點分析2
// 通過for循環,遍歷了當前ViewGroup下的所有子View
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
// 判斷當前遍歷的View是不是正在點擊的View,從而找到當前被點擊的View
// 若是,則進入條件判斷內部
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// 條件判斷的內部調用了該View的dispatchTouchEvent()
// 即 實現了點擊事件從ViewGroup到子View的傳遞(具體請看下面的View事件分發機制)
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
// 調用子View的dispatchTouchEvent後是有返回值的
// 若該控件可點擊,那麼點擊時,dispatchTouchEvent的返回值必定是true,因此會導致條件判斷成立
// 於是給ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
// 即把ViewGroup的點擊事件攔截掉
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
// 重點分析3
// 若點擊的是空白處(即無任何View接收事件) / 攔截事件(手動複寫onInterceptTouchEvent(),從而讓其返回true)
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
// 調用ViewGroup父類的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此會執行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己處理該事件,事件不會往下傳遞(具體請參考View事件的分發機制中的View.dispatchTouchEvent())
// 此處需與上面區別:子View的dispatchTouchEvent()
}
...
}
/**
* 分析1:ViewGroup.onInterceptTouchEvent()
* 作用:是否攔截事件
* 說明:
* a. 返回true = 攔截,即事件停止往下傳遞(需手動設置,即複寫onInterceptTouchEvent(),從而讓其返回true)
* b. 返回false = 不攔截(默認)
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
// 回到調用原處
3.2.2總結
- 結論:
Android
事件分發總是先傳遞到ViewGroup
、再傳遞到View
- 過程:當點擊了某個控件時
*核心方法總結
3.3 View事件的分發機制
從上面ViewGroup
事件分發機制知道,View
事件分發機制從dispatchTouchEvent(()
開始
3.3.1 源碼分析
/**
* 源碼分析:View.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
// 說明:只有以下3個條件都爲真,dispatchTouchEvent()才返回true;否則執行onTouchEvent()
// 1. mOnTouchListener != null
// 2. (mViewFlags & ENABLED_MASK) == ENABLED
// 3. mOnTouchListener.onTouch(this, event)
// 下面對這3個條件逐個分析
/**
* 條件1:mOnTouchListener != null
* 說明:mOnTouchListener變量在View.setOnTouchListener()方法裏賦值
*/
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
// 即只要我們給控件註冊了Touch事件,mOnTouchListener就一定被賦值(不爲空)
}
/**
* 條件2:(mViewFlags & ENABLED_MASK) == ENABLED
* 說明:
* a. 該條件是判斷當前點擊的控件是否enable
* b. 由於很多View默認enable,故該條件恆定爲true
*/
/**
* 條件3:mOnTouchListener.onTouch(this, event)
* 說明:即 回調控件註冊Touch事件時的onTouch();需手動複寫設置,具體如下(以按鈕Button爲例)
*/
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
// 若在onTouch()返回true,就會讓上述三個條件全部成立,從而使得View.dispatchTouchEvent()直接返回true,事件分發結束
// 若在onTouch()返回false,就會使得上述三個條件不全部成立,從而使得View.dispatchTouchEvent()中跳出If,執行onTouchEvent(event)
接下來,我們繼續看:OnTouchEvent(event)
的源碼分析
/**
* 源碼分析:View.onTouchEvent()
*/
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
// 若該控件可點擊,則進入switch判斷中
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
// a. 若當前的事件 = 擡起View(主要分析)
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
...// 經過種種判斷,此處省略
// 執行performClick() ->>分析1
performClick();
break;
// b. 若當前的事件 = 按下View
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
// c. 若當前的事件 = 結束事件(非人爲原因)
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break;
// d. 若當前的事件 = 滑動View
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
// 若該控件可點擊,就一定返回true
return true;
}
// 若該控件不可點擊,就一定返回false
return false;
}
/**
* 分析1:performClick()
*/
public boolean performClick() {
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
// 只要我們通過setOnClickListener()爲控件View註冊1個點擊事件
// 那麼就會給mOnClickListener變量賦值(即不爲空)
// 則會往下回調onClick() & performClick()返回true
}
return false;
}
3.3.2 總結
- 每當控件被點擊時:
注:onTouch()
的執行先於onClick()
* 核心方法總結
3.3.3 Demo講解
下面我將用Demo驗證上述的結論
/**
* 結論驗證1:在回調onTouch()裏返回false
*/
// 1. 通過OnTouchListener()複寫onTouch(),從而手動設置返回false
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("執行了onTouch(), 動作是:" + event.getAction());
return false;
}
});
// 2. 通過 OnClickListener()爲控件設置點擊事件,爲mOnClickListener變量賦值(即不爲空),從而往下回調onClick()
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("執行了onClick()");
}
});
/**
* 結論驗證2:在回調onTouch()裏返回true
*/
// 1. 通過OnTouchListener()複寫onTouch(),從而手動設置返回true
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("執行了onTouch(), 動作是:" + event.getAction());
return true;
}
});
// 2. 通過 OnClickListener()爲控件設置點擊事件,爲mOnClickListener變量賦值(即不爲空)
// 但由於dispatchTouchEvent()返回true,即事件不再向下傳遞,故不調用onClick())
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("執行了onClick()");
}
});
測試結果
3.3.4 總結
4.工作流程總結
- 事件分發的工作流程總結:
*以角色爲核心的圖解說明
- 以方法爲核心的圖解說明
5.核心方法總結
- 已知事件分發過程的核心方法爲:
dispatchTouchEvent()
、onInterceptTouchEvent()
和OnTouchEvent()
- 下面,詳細講解該3個方法
4.1 dispatchTouchEvent()
- 簡介
- 返回情況說明
情況1:默認
情況2:返回true
情況3:返回false
5.2 onInterceptTouchEvent()
- 簡介
注:在Activity
、View
中無該方法
返回情況說明
情況1:true
情況2:false(默認)
5.3 onTouchEvent()
- 簡介
返回情況說明
情況1:返回true
情況2:返回false(默認)
5.4 三者關係
下面,用一段僞代碼來闡述上述3個方法的關係和時間傳遞規則
/**
* 點擊事件產生後
*/
// 步驟1:調用dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false; //代表 是否會消費事件
// 步驟2:判斷是否攔截事件
if (onInterceptTouchEvent(ev)) {
// a. 若攔截,則將該事件交給當前View進行處理
// 即調用onTouchEvent ()方法去處理點擊事件
consume = onTouchEvent (ev) ;
} else {
// b. 若不攔截,則將該事件傳遞到下層
// 即 下層元素的dispatchTouchEvent()就會被調用,重複上述過程
// 直到點擊事件被最終處理爲止
consume = child.dispatchTouchEvent (ev) ;
}
// 步驟3:最終返回通知 該事件是否被消費(接收 & 處理)
return consume;
}
6.常見的事件分發場景
下面,將通過實例說明常見的事件傳遞情況和流程
6.1 背景描述
- 討論的佈局如下:
- 情景
1.用戶先觸摸到屏幕上View c
上的某個點(圖中黃區)
Action_DOWN
事件在此處產生
2.用戶移動手
3.最後離開屏幕
6.2 一般的 事件傳遞情況
- 默認情況
- 處理事件
- 攔截
DOWN
事件 - 攔截後續事件(
MOVE
、UP
)
場景1: 默認
- 即不對控件裏的方法(
dispatchTouchEvent()
、onTouchEvent()
、onInterceptTouchEvent()
)進行重寫或更改返回值 - 那麼調用的試着3個方法的默認實現;調用下層的方法的&逐層返回
- 事件傳遞情況:
1.從上往下調用dispatchTouchEvent()
ActivityA——>ViewGroupB——>View C
2.從下往上調用onTouchEvent()
View C——>ViewGroup B——>Activity A
注:雖然ViewGroup B
的onInterceptTouchEvent()
對DOWN
事件返回了false
,但後續的事件(MOVE
、UP
)依然會傳遞給它的onInterceptTouchEvent()
這一點與onTouchEvent()
的行爲是不一樣的:不再傳遞&接受該事件列的其它事件
場景2 :處理事件
設View C
希望處理該點擊事件,即:設置View C
爲可點擊的(Clickable
)或複寫其onTouchEvent()
返回true
最常見的:設置Button
按鈕來響應點擊事件
事件傳遞情況:(如下圖)
DOWN
事件被傳遞給C的onTouchEvent()
方法,該方法返回true
,表示處理該事件- 因爲
view c
正在處理該事件,那麼DOWN
事件將不再傳遞給ViewGroup B
和Activity A
的onTouchEvent()
; - 該事件列的其他事件(```MOVE
,
UP)也將傳遞給
View C的
onTouchEvent(); ![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190429001454778.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h5enNvMXo=,size_16,color_FFFFFF,t_70) 會逐層往
dispathchtouchEvent()``返回,最終事件分發結束
場景3 :攔截DOWN事件
假設ViewGroup B`希望處理該點擊事件,即
ViewGroup B複寫
onInterceptTouchEvent()返回
true,
onTouchEvent()返回
true``
事件傳遞情況:
DOWN
事件被傳遞給ViewGroup B
的onInterceptTouchEvent()
,該方法返回true
,表示攔截該事件,即自己處理該事件(事件不再往下傳遞)- 調用自身的
onTouchEvent()
處理事件(DOWN
事件將不再往上傳遞給Activity A
的onTouchEvent()
) - 該事件列的其他事件(
Move
、Up
)將直接傳遞給ViewGroup B
的`onTouchEvent()注: 1.該事件列的其他事件(
Move、
Up)將不會再傳遞給
ViewGroup B的
onInterceptTouchEvent(),因爲該方法一旦返回一次
true,就再也不會被調用 2.逐層往```dispatchTouchEvent()
返回,最終事件分發結束
場景4:攔截DOWN的後續事件
結論
- 若
ViewGroup
攔截了一個半路的事件(如Move
),該事件將會被系統變成一個CANCELL
事件&傳遞給之前處理該事件的子View
; - 該事件不會再傳遞給
ViewGroup
的onTouchEvent()
- 只有再到來的事件纔會傳遞到
ViewGroup
的onTouchEvent()
場景描述
ViewGroup B
無法攔截DOWN
事件(還是View C
來處理Down
事件),但它攔截了接下來的Move
事件
即Down
事件傳遞到View C
的onTouchEvent()
,返回true
實例講解 - 在後續到來的
MOVE
事件,ViewGroup B
的onInterceptTouchEvent()
返回true
攔截該MOVE
事件,但該事件並沒有傳遞給ViewGroup B
;這個MOVE
事件將會被系統變成一個CANCEL
事件傳遞給View C
的onTouchEvent()
; - 後續又來了一個
MOVE
事件,該MOVE
事件纔會直接傳遞給ViewGroup B
的onTouchEvent()
後續事件將直接傳遞給
ViewGroup B
的onTouchEvent()
處理,而不會傳遞給ViewGroup B
的onInterceptTouchEvent()
,因該方法一旦返回一次true,就再也不會被調用了。
View C
再也不會收到該事件列產生的後續事件
7.額外知識
7.1 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
事件的傳遞結論:
- 結論1
若對象(Activity、ViewGroup、View)的dispatchTouchEvent()分發事件後消費了事件(返回true),那麼收到ACTION_DOWN的函數也能收到ACTION_MOVE和ACTION_UP - 結論2
若對象(Activity、ViewGroup、View)的onTouchEvent()處理了事件(返回true),那麼ACTION_MOVE、ACTION_UP的事件從上往下傳到該View後就不再往下傳遞,而是直接傳給自己的onTouchEvent,結束本次事件傳遞的過程。