總結(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)鍵
- 其對Down事件的返回值表征是否消費該事件(對其它事件的返回值無所謂)
- 自上而下分發(fā),采用逐層負(fù)責(zé)制
- 分發(fā)之前先判斷是否要攔截
2.2.2 onInterceptTouchEvent
onInterceptTouchEvent定義在ViewGroup中侦啸,用于攔截事件槽唾。
onInterceptTouchEvent被調(diào)用需要滿足兩個條件:
- 當(dāng)前傳遞的是Down事件或者有子View消費了之前的Down事件
- 允許攔截
如果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)歷了如下步驟:
- 首先產(chǎn)生的是Down事件殴瘦,傳遞給L.dispatchTouchEvent
- L隨之調(diào)用自身的onInterceptTouchEvent方法
- L.onInterceptTouchEvent返回false狠角,表明L不攔截
- L于是把事件分發(fā)給R(調(diào)用R.dispatchTouchEvent)
- 同樣的,R先調(diào)用自身的onInterceptTouchEvent
- R.onInterceptTouchEvent返回false蚪腋,表明R也不攔截
- R于是把事件分發(fā)給T1(調(diào)用T1.dispatchTouchEvent)
- T1是普通View擎厢,不再需要向下分發(fā)究流,于是調(diào)用自身的onTouchEvent
(自此,事件分發(fā)完成动遭,開始處理事件) - T1.onTouchEvent返回false
- T1.dispatchTouchEvent收到T1.onTouchEvent的返回值芬探,同樣返回false,表明T1不消費Down事件
(第9厘惦、10步參見1.3節(jié)View處理機制) - R根據(jù)T1.dispatchTouchEvent的返回值判斷T1沒有消費事件偷仿,于是嘗試自己處理該事件,調(diào)用自身的onTouchEvent
- R.onTouchEvent返回false
- 從而導(dǎo)致R.dispatchTouchEvent返回false宵蕉,表明R不消費Down事件
- 由于R沒有消費事件酝静,L嘗試自己處理事件,調(diào)用自身的onTouchEvent
- L.onTouchEvent返回false
- 從而導(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)嘀略。
做兩個實驗驗證:
- 令T1.onTouchEvent返回true恤溶,而T1.dispatchTouchEvent返回false
- 令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):
- 令T1.dispatchTouchEvent返回false(默認(rèn)情況)蹲缠,R.dispatchTouchEvent返回true(點擊T1)
- 令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ù)事件。
攔截機制
做兩個實驗:
- (攔截Down事件)令L.onInterceptTouchEvent(ACTION_DOWN)返回true钳踊,同時L.dispatchTouchEvent(ACTION_DOWN)返回true衷敌,其它默認(rèn)(點擊T1)
- (攔截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至6)L攔截了Down事件拓瞪,事件由L進(jìn)行處理缴罗,不再向下分發(fā)
- (日志7至10)系統(tǒng)下發(fā)Up事件給L后,L不再調(diào)用自身的onInterceptTouchEvent祭埂。
實驗2日志分析:
- L攔截了第一個Move事件
- 該事件轉(zhuǎn)為Cancel事件面氓,繼續(xù)向下分發(fā)
- L.onTouchEvent不會被調(diào)用
- 后續(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