Android事件傳遞機制

總結(jié)覆旭,細(xì)化浪听,推演

一、總結(jié)

四個事件楣嘁,三個方法,兩套機制

1.1 四個事件

  • Down
  • Move
  • Up/Cancel

1.2 三個方法

  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent

1.3 兩套機制

Talk is cheap, show me the code.

ViewGroup處理機制:

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (!onInterceptTouchEvent(ev)) {                   // 1. 判斷是否攔截
        for (int i = childrenCount - 1; i >= 0; i--) {  // 2. 逆序遍歷子View珍逸,尋找意欲消費該事件的子View
            if (children[i].dispatchTouchEvent(ev)) {
                return true;
            }
        }
    }
    return super.dispatchTouchEvent(ev);               // 3. 如果沒有子View消費事件逐虚,則走View處理機制
}

View處理機制:

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (onTouchListener != null) {
        return onTouchListener.onTouch(ev) || onTouchEvent(ev);
    }
    return onTouchEvent(ev);
}

注意
1、上述機制是極度簡化后的(后文詳述)
2谆膳、ViewGroup本身也是View

二叭爱、細(xì)化

2.1 四個事件

只考慮單點觸控的情況下,一次手勢操作產(chǎn)生的事件序列如下圖所示:


基本事件序列

注意
事件消費以手勢為單位漱病,如果View不消費Down事件买雾,則它將不再收到其它事件;反之杨帽,如果它消費了Down事件漓穿,則必然以一個Up或Cancel事件結(jié)束。

2.2 三個方法

dispatchTouchEvent注盈、onInterceptTouchEvent和onTouchEvent都接收一個TouchEvent作為參數(shù)器净,返回值類型都是boolean。

dispatchTouchEvent自上而下分發(fā)当凡,onTouchEvent自下而上處理山害,onInterceptTouchEvent居中攔截,構(gòu)成了Android事件的處理機制沿量。

2.2.1 dispatchTouchEvent

dispatchTouchEvent定義在Activity浪慌、ViewGroup和View中,控制TouchEvent走向朴则,其目的就是找出哪個View應(yīng)該處理該TouchEvent权纤,簡言之,它用于事件的分發(fā)乌妒。

Activity.dispatchTouchEvent總是最先收到事件汹想,經(jīng)過一系列下發(fā)后,傳遞給布局撤蚊。本文只考慮布局對手勢事件的處理古掏。

dispatchTouchEvent有幾個原則非常關(guān)鍵

  1. 其對Down事件的返回值表征是否消費該事件(對其它事件的返回值無所謂)
  2. 自上而下分發(fā),采用逐層負(fù)責(zé)制
  3. 分發(fā)之前先判斷是否要攔截
2.2.2 onInterceptTouchEvent

onInterceptTouchEvent定義在ViewGroup中侦啸,用于攔截事件槽唾。

onInterceptTouchEvent被調(diào)用需要滿足兩個條件:

  1. 當(dāng)前傳遞的是Down事件或者有子View消費了之前的Down事件
  2. 允許攔截

如果onInterceptTouchEvent攔截的是Down事件丧枪,Down事件不會繼續(xù)向下傳遞;如果攔截的是Move或Up事件庞萍,則會下發(fā)Cancel事件給下層拧烦。

onInterceptTouchEvent一旦返回true,就不會再調(diào)用钝计。

2.2.3 onTouchEvent

onTouchEvent定義在Activity恋博、ViewGroup和View中,消費事件私恬。

2.3 兩套機制

1.3節(jié)中所示的機制確切來說是不正確的债沮,實際的事件傳遞機制要復(fù)雜得多。

Android事件處理涉及三個操作:分發(fā)践付、攔截與消費秦士。

  • 分發(fā)
  • dispatchTouchEvent負(fù)責(zé)分發(fā)
  • 分發(fā)自上而下
  • 分發(fā)前判斷是否攔截
  • dispatchTouchEvent對Down事件返回true表征View/ViewGroup消費該事件
  • dispatchTouchEvent對其余事件的返回值對分發(fā)沒有任何影響
  • ViewGroup會逆序遍歷邊界包含觸控點的所有子View缺厉,調(diào)用其dispatchTouchEvent判斷其是否消費
  • 一旦找到消費事件的子View
    • ViewGroup會停止遍歷
    • 后續(xù)事件會直接分發(fā)給該子View永高,不再遍歷
  • 攔截
  • onInterceptTouchEvent負(fù)責(zé)攔截
  • onInterceptTouchEvent返回true表征攔截
  • 如果ViewGroup A攔截Down事件
    • 所有事件由攔截事件的ViewGroup處理,不向下分發(fā)
  • 如果ViewGroup A攔截Move/Up事件
    • 事件轉(zhuǎn)為Cancel向下分發(fā)
    • A.onTouchEvent在本次事件中不會被調(diào)用
    • A.dispatchTouchEvent會將后續(xù)事件直接傳遞給A.onTouchEvent提针,不向下分發(fā)
  • 一旦onInterceptTouchEvent返回true命爬,就不會再被調(diào)用
  • 消費
    • OnTouchListener或onTouchEvent負(fù)責(zé)消費
    • OnTouchListener優(yōu)先于onTouchEvent
    • 如果OnTouchListener.onTouch返回true,則onTouchEvent不會再被調(diào)用
    • 如果沒有注冊O(shè)nTouchListener或OnTouchListener.onTouch返回false辐脖,onTouchEvent會再被調(diào)用

第三章通過推演詳述這兩套機制饲宛。

三、推演

假定有一個如下布局:L里包了一個R嗜价,R里包了B艇抠、T1、T2三個子View久锥,T2疊在T1上家淤。



L繼承自LinearLayout,R繼承自RelativeLayout瑟由,B繼承自Button絮重,T1、T2繼承自TextView歹苦,在它們各事件方法的頭部和尾部分別插入日志以觀察其行為:

// public boolean onInterceptTouchEvent(MotionEvent ev) {
// public boolean onTouchEvent(MotionEvent event) {
public boolean dispatchTouchEvent(MotionEvent ev) {
    L.e(MotionEvent.actionToString(ev.getAction()));
    boolean result = super.dispatchTouchEvent(ev);
    L.e(MotionEvent.actionToString(ev.getAction()) + " " + result);
    return result;
}

默認(rèn)情況
在T1的右側(cè)(未被T2覆蓋的部分)滑動手指青伤,生成如下日志:

1. L.dispatchTouchEvent(L.java:41) ==> ACTION_DOWN
2. L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3. L.onInterceptTouchEvent(L.java:35) ==> ACTION_DOWN false
4.  R.dispatchTouchEvent(R.java:45) ==> ACTION_DOWN
5.  R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN
6.  R.onInterceptTouchEvent(R.java:39) ==> ACTION_DOWN false
7.      T1.dispatchTouchEvent(T1.java:35) ==> ACTION_DOWN
8.      T1.onTouchEvent(T1.java:43) ==> ACTION_DOWN
9.      T1.onTouchEvent(T1.java:45) ==> ACTION_DOWN false
10.     T1.dispatchTouchEvent(T1.java:37) ==> ACTION_DOWN false
11. R.onTouchEvent(R.java:53) ==> ACTION_DOWN
12. R.onTouchEvent(R.java:55) ==> ACTION_DOWN false
13. R.dispatchTouchEvent(R.java:47) ==> ACTION_DOWN false
14. L.onTouchEvent(L.java:49) ==> ACTION_DOWN
15. L.onTouchEvent(L.java:51) ==> ACTION_DOWN false
16. L.dispatchTouchEvent(L.java:43) ==> ACTION_DOWN false

從日志中可以看出,事件傳遞經(jīng)歷了如下步驟:

  1. 首先產(chǎn)生的是Down事件殴瘦,傳遞給L.dispatchTouchEvent
  2. L隨之調(diào)用自身的onInterceptTouchEvent方法
  3. L.onInterceptTouchEvent返回false狠角,表明L不攔截
  4. L于是把事件分發(fā)給R(調(diào)用R.dispatchTouchEvent)
  5. 同樣的,R先調(diào)用自身的onInterceptTouchEvent
  6. R.onInterceptTouchEvent返回false蚪腋,表明R也不攔截
  7. R于是把事件分發(fā)給T1(調(diào)用T1.dispatchTouchEvent)
  8. T1是普通View擎厢,不再需要向下分發(fā)究流,于是調(diào)用自身的onTouchEvent
    (自此,事件分發(fā)完成动遭,開始處理事件)
  9. T1.onTouchEvent返回false
  10. T1.dispatchTouchEvent收到T1.onTouchEvent的返回值芬探,同樣返回false,表明T1不消費Down事件
    (第9厘惦、10步參見1.3節(jié)View處理機制)
  11. R根據(jù)T1.dispatchTouchEvent的返回值判斷T1沒有消費事件偷仿,于是嘗試自己處理該事件,調(diào)用自身的onTouchEvent
  12. R.onTouchEvent返回false
  13. 從而導(dǎo)致R.dispatchTouchEvent返回false宵蕉,表明R不消費Down事件
  14. 由于R沒有消費事件酝静,L嘗試自己處理事件,調(diào)用自身的onTouchEvent
  15. L.onTouchEvent返回false
  16. 從而導(dǎo)致L.dispatchTouchEvent返回false
    (由于Down事件沒有被消費羡玛,因此它們都將不再收到其它事件别智,事件處理結(jié)束)

結(jié)論
1、dispatchTouchEvent自上而下分發(fā)
2稼稿、onTouchEvent自下而上消費
3薄榛、ViewGroup.dispatchTouchEvent向下分發(fā)之前,會先調(diào)用onInterceptTouchEvent
4让歼、如果Down事件沒有被消費敞恋,將不再收到其余事件(Move,Up)

消費事件
如果View是clickable的谋右,或者View注冊了OnTouchListener且其onTouch方法返回true硬猫,或者實現(xiàn)了自定義的返回值為true的onTouchEvent,則認(rèn)為View消費了事件改执。

根據(jù)我們的假定啸蜜,B繼承自Button,它是clickable的辈挂,因此可用于做消費事件的實驗衬横,但為了同時說明ViewGroup尋找子View的機制,我們在T1上注冊一個OnTouchListener回調(diào)并返回true呢岗。

在T2上點擊冕香,日志如下:

1. L.dispatchTouchEvent(L.java:41) ==> ACTION_DOWN
2. L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3. L.onInterceptTouchEvent(L.java:35) ==> ACTION_DOWN false
4.     R.dispatchTouchEvent(R.java:43) ==> ACTION_DOWN
5.     R.onInterceptTouchEvent(R.java:35) ==> ACTION_DOWN
6.     R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN false
7.          T2.dispatchTouchEvent(T2.java:35) ==> ACTION_DOWN
8.          T2.onTouchEvent(T2.java:43) ==> ACTION_DOWN
9.          T2.onTouchEvent(T2.java:45) ==> ACTION_DOWN false
10.         T2.dispatchTouchEvent(T2.java:37) ==> ACTION_DOWN false
11.         T1.dispatchTouchEvent(T1.java:35) ==> ACTION_DOWN
12.         MainActivity$1.onTouch(MainActivity.java:21) ==> ACTION_DOWN true
13.         T1.dispatchTouchEvent(T1.java:37) ==> ACTION_DOWN true
14.    R.dispatchTouchEvent(R.java:45) ==> ACTION_DOWN true
15. L.dispatchTouchEvent(L.java:43) ==> ACTION_DOWN true

16. L.dispatchTouchEvent(L.java:41) ==> ACTION_UP
17. L.onInterceptTouchEvent(L.java:33) ==> ACTION_UP
18. L.onInterceptTouchEvent(L.java:35) ==> ACTION_UP false
19.     R.dispatchTouchEvent(R.java:43) ==> ACTION_UP
20.     R.onInterceptTouchEvent(R.java:35) ==> ACTION_UP
21.     R.onInterceptTouchEvent(R.java:37) ==> ACTION_UP false
22.         T1.dispatchTouchEvent(T1.java:35) ==> ACTION_UP
23.         MainActivity$1.onTouch(MainActivity.java:21) ==> ACTION_UP true
24.         T1.dispatchTouchEvent(T1.java:37) ==> ACTION_UP true
25.     R.dispatchTouchEvent(R.java:45) ==> ACTION_UP true
26. L.dispatchTouchEvent(L.java:43) ==> ACTION_UP true

日志分析:
日志1至日志10與默認(rèn)情況無異,下面主要說說幾處不同后豫。
11至13:事件透過T2繼續(xù)向T1分發(fā)悉尾,且T1注冊的回調(diào)返回了true,因此T1消費了Down事件
14至15:由于有子View消費了事件挫酿,因此L构眯、R的onTouchEvent方法沒有被調(diào)用
16至26:由于Down事件被消費,因此系統(tǒng)繼續(xù)下發(fā)后續(xù)事件(Up)
(手指滑動小于20像素早龟,不會產(chǎn)生Move事件惫霸,本例為簡化沒有令其產(chǎn)生)
另外注意猫缭,R沒有將Up事件傳遞給T2,而是直接傳給了T1

結(jié)論
1壹店、如果消費了Down事件猜丹,則會繼續(xù)收到后續(xù)事件
2、ViewGroup會以子View添加順序的逆序查找邊界包含觸控點的所有子View
(2.1 事實上硅卢,如果遍歷時發(fā)現(xiàn)某子View消費了該事件射窒,ViewGroup會停止遍歷)
3、一旦查找到消費事件的子View将塑,后續(xù)事件會直接傳遞給它脉顿,不會再遍歷

關(guān)于結(jié)論中2.1的論證,可以做這樣一個實驗:
在T2上注冊O(shè)nTouchListener回調(diào)点寥,實驗方式不變艾疟,會發(fā)現(xiàn)事件不會傳遞給T1,而是由T2消費Down和Up事件敢辩。

如何判斷事件是否被消費蔽莱?
關(guān)于事件是否被消費,有說法說是看onTouchEvent是否返回true责鳍,這是不對的碾褂,實際上要看dispatchTouchEvent是否返回true兽间,之所以會有這種說法历葛,是因為默認(rèn)情況下如果onTouchEvent返回true,則dispatchTouchEvent必然返回true(見1.3)嘀略。

做兩個實驗驗證:

  1. 令T1.onTouchEvent返回true恤溶,而T1.dispatchTouchEvent返回false
  2. 令T1.onTouchEvent返回false,而T1.dispatchTouchEvent返回true

實驗一日志輸出除第9條外與默認(rèn)情況完全一樣帜羊,這里從略咒程。
實驗二日志如下:

1. L.dispatchTouchEvent(L.java:41) ==> ACTION_DOWN
2. L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3. L.onInterceptTouchEvent(L.java:35) ==> ACTION_DOWN false
4.  R.dispatchTouchEvent(R.java:45) ==> ACTION_DOWN
5.  R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN
6.  R.onInterceptTouchEvent(R.java:39) ==> ACTION_DOWN false
7.          T1.dispatchTouchEvent(T1.java:35) ==> ACTION_DOWN
8.          T1.onTouchEvent(T1.java:44) ==> ACTION_DOWN
9.          T1.onTouchEvent(T1.java:46) ==> ACTION_DOWN false
10.         T1.dispatchTouchEvent(T1.java:38) ==> ACTION_DOWN true
11.     R.dispatchTouchEvent(R.java:47) ==> ACTION_DOWN true
12. L.dispatchTouchEvent(L.java:43) ==> ACTION_DOWN true

13. L.dispatchTouchEvent(L.java:41) ==> ACTION_UP
14. L.onInterceptTouchEvent(L.java:33) ==> ACTION_UP
15. L.onInterceptTouchEvent(L.java:35) ==> ACTION_UP false
16.     R.dispatchTouchEvent(R.java:45) ==> ACTION_UP
17.     R.onInterceptTouchEvent(R.java:37) ==> ACTION_UP
18.     R.onInterceptTouchEvent(R.java:39) ==> ACTION_UP false
19.         T1.dispatchTouchEvent(T1.java:35) ==> ACTION_UP
20.         T1.onTouchEvent(T1.java:44) ==> ACTION_UP
21.         T1.onTouchEvent(T1.java:46) ==> ACTION_UP false
22.         T1.dispatchTouchEvent(T1.java:38) ==> ACTION_UP true
23.     R.dispatchTouchEvent(R.java:47) ==> ACTION_UP true
24. L.dispatchTouchEvent(L.java:43) ==> ACTION_UP true

從日志中可以看出,
(實驗1)雖然T1.onTouchEvent返回true讼育,但由于T1.dispatchTouchEvent返回false帐姻,系統(tǒng)仍然認(rèn)為T1沒有消費Down事件,因此T1不再收到后續(xù)事件奶段。
(實驗2)反之饥瓷,強制使T1.dispatchTouchEvent返回true后,T1(及T1的父控件L痹籍、R)會收到后續(xù)事件(Up)呢铆,
由此可知

dispatchTouchEvent返回true <=> View消費了事件

逐層負(fù)責(zé)制
為了說明事件下發(fā)是采用逐層負(fù)責(zé)制,做兩個實驗(點擊T1):

  1. 令T1.dispatchTouchEvent返回false(默認(rèn)情況)蹲缠,R.dispatchTouchEvent返回true(點擊T1)
  2. 令R.dispatchTouchEvent返回false棺克,L.dispatchTouchEvent返回true(點擊B)

實驗1日志如下:

1.  L.dispatchTouchEvent(L.java:41) ==> ACTION_DOWN
2.  L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3.  L.onInterceptTouchEvent(L.java:35) ==> ACTION_DOWN false
4.      R.dispatchTouchEvent(R.java:43) ==> ACTION_DOWN
5.      R.onInterceptTouchEvent(R.java:35) ==> ACTION_DOWN
6.      R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN false
7.          T1.dispatchTouchEvent(T1.java:35) ==> ACTION_DOWN
8.          T1.onTouchEvent(T1.java:43) ==> ACTION_DOWN
9.          T1.onTouchEvent(T1.java:45) ==> ACTION_DOWN false
10.         T1.dispatchTouchEvent(T1.java:37) ==> ACTION_DOWN false
11.     R.onTouchEvent(R.java:52) ==> ACTION_DOWN
12.     R.onTouchEvent(R.java:54) ==> ACTION_DOWN false
13.     R.dispatchTouchEvent(R.java:46) ==> ACTION_DOWN true
14. L.dispatchTouchEvent(L.java:43) ==> ACTION_DOWN true

15. L.dispatchTouchEvent(L.java:41) ==> ACTION_UP
16. L.onInterceptTouchEvent(L.java:33) ==> ACTION_UP
17. L.onInterceptTouchEvent(L.java:35) ==> ACTION_UP false
18.     R.dispatchTouchEvent(R.java:43) ==> ACTION_UP
19.     R.onTouchEvent(R.java:52) ==> ACTION_UP
20.     R.onTouchEvent(R.java:54) ==> ACTION_UP false
21.     R.dispatchTouchEvent(R.java:46) ==> ACTION_UP true
22. L.dispatchTouchEvent(L.java:43) ==> ACTION_UP true

實驗2日志如下:

1.  L.dispatchTouchEvent(L.java:41) ==> ACTION_DOWN
2.  L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3.  L.onInterceptTouchEvent(L.java:35) ==> ACTION_DOWN false
4.      R.dispatchTouchEvent(R.java:43) ==> ACTION_DOWN
5.      R.onInterceptTouchEvent(R.java:35) ==> ACTION_DOWN
6.      R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN false
7.          B.dispatchTouchEvent(B.java:35) ==> ACTION_DOWN
8.          B.onTouchEvent(B.java:43) ==> ACTION_DOWN
9.          B.onTouchEvent(B.java:45) ==> ACTION_DOWN true
10.         B.dispatchTouchEvent(B.java:37) ==> ACTION_DOWN true
11.     R.dispatchTouchEvent(R.java:46) ==> ACTION_DOWN false
12. L.onTouchEvent(L.java:50) ==> ACTION_DOWN
13. L.onTouchEvent(L.java:52) ==> ACTION_DOWN false
14. L.dispatchTouchEvent(L.java:44) ==> ACTION_DOWN true

15. L.dispatchTouchEvent(L.java:41) ==> ACTION_UP
16. L.onTouchEvent(L.java:50) ==> ACTION_UP
17. L.onTouchEvent(L.java:52) ==> ACTION_UP false
18. L.dispatchTouchEvent(L.java:44) ==> ACTION_UP true

從實驗1日志可以看出悠垛,T1沒有消費Down事件,R消費了Down事件娜谊,所以Up事件繼續(xù)傳遞給R确买,但R不再向T1分發(fā)。
從實驗2日志可以看出纱皆,雖然B消費了Down事件拇惋,但由于R.dispatchTouchEvent返回了false,表征R沒有消費Down事件抹剩,因此系統(tǒng)不再向R下發(fā)后續(xù)事件撑帖,也就不會向B下發(fā)后續(xù)事件。

結(jié)論
如果ViewGroup沒有消費Down事件澳眷,即使其子View消費了Down事件胡嘿,該ViewGroup及其子View都不會再收到后續(xù)事件。

攔截機制
做兩個實驗:

  1. (攔截Down事件)令L.onInterceptTouchEvent(ACTION_DOWN)返回true钳踊,同時L.dispatchTouchEvent(ACTION_DOWN)返回true衷敌,其它默認(rèn)(點擊T1)
  2. (攔截Move事件)令L.onInterceptTouchEvent(ACTION_MOVE)返回true,其它默認(rèn)(點擊B)

實驗1日志輸出如下:

1.  L.dispatchTouchEvent(L.java:44) ==> ACTION_DOWN
2.  L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3.  L.onInterceptTouchEvent(L.java:38) ==> ACTION_DOWN true
4.  L.onTouchEvent(L.java:55) ==> ACTION_DOWN
5.  L.onTouchEvent(L.java:57) ==> ACTION_DOWN false
6.  L.dispatchTouchEvent(L.java:49) ==> ACTION_DOWN true

7.  L.dispatchTouchEvent(L.java:44) ==> ACTION_UP
8.  L.onTouchEvent(L.java:55) ==> ACTION_UP
9.  L.onTouchEvent(L.java:57) ==> ACTION_UP false
10. L.dispatchTouchEvent(L.java:49) ==> ACTION_UP false

實驗2日志輸出如下:

1.  L.dispatchTouchEvent(L.java:44) ==> ACTION_DOWN
2.  L.onInterceptTouchEvent(L.java:33) ==> ACTION_DOWN
3.  L.onInterceptTouchEvent(L.java:38) ==> ACTION_DOWN false
4.      R.dispatchTouchEvent(R.java:43) ==> ACTION_DOWN
5.      R.onInterceptTouchEvent(R.java:35) ==> ACTION_DOWN
6.      R.onInterceptTouchEvent(R.java:37) ==> ACTION_DOWN false
7.          B.dispatchTouchEvent(B.java:35) ==> ACTION_DOWN
8.          B.onTouchEvent(B.java:43) ==> ACTION_DOWN
9.          B.onTouchEvent(B.java:45) ==> ACTION_DOWN true
10.         B.dispatchTouchEvent(B.java:37) ==> ACTION_DOWN true
11.     R.dispatchTouchEvent(R.java:45) ==> ACTION_DOWN true
12. L.dispatchTouchEvent(L.java:46) ==> ACTION_DOWN true

13. L.dispatchTouchEvent(L.java:44) ==> ACTION_MOVE
14. L.onInterceptTouchEvent(L.java:33) ==> ACTION_MOVE
15. L.onInterceptTouchEvent(L.java:38) ==> ACTION_MOVE true
16.     R.dispatchTouchEvent(R.java:43) ==> ACTION_CANCEL
17.     R.onInterceptTouchEvent(R.java:35) ==> ACTION_CANCEL
18.     R.onInterceptTouchEvent(R.java:37) ==> ACTION_CANCEL false
19.         B.dispatchTouchEvent(B.java:35) ==> ACTION_CANCEL
20.         B.onTouchEvent(B.java:43) ==> ACTION_CANCEL
21.         B.onTouchEvent(B.java:45) ==> ACTION_CANCEL true
22.         B.dispatchTouchEvent(B.java:37) ==> ACTION_CANCEL true
23.     R.dispatchTouchEvent(R.java:45) ==> ACTION_CANCEL true
24. L.dispatchTouchEvent(L.java:46) ==> ACTION_MOVE true

25. L.dispatchTouchEvent(L.java:44) ==> ACTION_MOVE
26. L.onTouchEvent(L.java:52) ==> ACTION_MOVE
27. L.onTouchEvent(L.java:54) ==> ACTION_MOVE false
28. L.dispatchTouchEvent(L.java:46) ==> ACTION_MOVE false

29. L.dispatchTouchEvent(L.java:44) ==> ACTION_UP
30. L.onTouchEvent(L.java:52) ==> ACTION_UP
31. L.onTouchEvent(L.java:54) ==> ACTION_UP false
32. L.dispatchTouchEvent(L.java:46) ==> ACTION_UP false

實驗1日志分析:

  1. (日志1至6)L攔截了Down事件拓瞪,事件由L進(jìn)行處理缴罗,不再向下分發(fā)
  2. (日志7至10)系統(tǒng)下發(fā)Up事件給L后,L不再調(diào)用自身的onInterceptTouchEvent祭埂。

實驗2日志分析:

  1. L攔截了第一個Move事件
  • 該事件轉(zhuǎn)為Cancel事件面氓,繼續(xù)向下分發(fā)
  • L.onTouchEvent不會被調(diào)用
  1. 后續(xù)事件(Move/Up)直接由L處理
  • 不再調(diào)用onInterceptTouchEvent
  • 也不再向下分發(fā)

結(jié)論
1、 如果ViewGroup攔截了Down事件蛆橡,則該ViewGroup不向下分發(fā)任何事件舌界,該ViewGroup會消費整個手勢事件。
2泰演、如果ViewGroup攔截了Move事件呻拌,則該事件會轉(zhuǎn)為Cancel事件繼續(xù)向下分發(fā),但不會調(diào)用自身的onTouchEvent睦焕,而后續(xù)的事件則會直接有該ViewGroup處理藐握,不在詢問是否攔截,也不再向下分發(fā)
3垃喊、一旦onInterceptTouchEvent返回true猾普,就不會再被調(diào)用
4、 如果子View沒有消費事件而ViewGroup消費了Down事件缔御,則后續(xù)不會再調(diào)用ViewGroup的onInterceptTouchEvent

拓展閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抬闷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笤成,老刑警劉巖评架,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異炕泳,居然都是意外死亡纵诞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門培遵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浙芙,“玉大人,你說我怎么就攤上這事籽腕∥撕簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵皇耗,是天一觀的道長南窗。 經(jīng)常有香客問我,道長郎楼,這世上最難降的妖魔是什么万伤? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮呜袁,結(jié)果婚禮上敌买,老公的妹妹穿的比我還像新娘。我一直安慰自己阶界,他們只是感情好虹钮,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荐操,像睡著了一般芜抒。 火紅的嫁衣襯著肌膚如雪珍策。 梳的紋絲不亂的頭發(fā)上托启,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音攘宙,去河邊找鬼屯耸。 笑死,一個胖子當(dāng)著我的面吹牛蹭劈,可吹牛的內(nèi)容都是我干的疗绣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼铺韧,長吁一口氣:“原來是場噩夢啊……” “哼多矮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤塔逃,失蹤者是張志新(化名)和其女友劉穎讯壶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體湾盗,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡伏蚊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了格粪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躏吊。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帐萎,靈堂內(nèi)的尸體忽然破棺而出比伏,到底是詐尸還是另有隱情,我是刑警寧澤疆导,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布凳怨,位于F島的核電站,受9級特大地震影響是鬼,放射性物質(zhì)發(fā)生泄漏肤舞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一均蜜、第九天 我趴在偏房一處隱蔽的房頂上張望李剖。 院中可真熱鬧,春花似錦囤耳、人聲如沸篙顺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽德玫。三九已至,卻和暖如春椎麦,著一層夾襖步出監(jiān)牢的瞬間宰僧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工观挎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留琴儿,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓嘁捷,卻偏偏與公主長得像造成,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雄嚣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容