1. 程式人生 > >可能是最詳細的Android點選事件處理詳解(二)

可能是最詳細的Android點選事件處理詳解(二)

上一篇我們主要詳細描述了touch事件在各層的傳遞
本篇文章主要是對比touch在不可滾動和可滾動的ViewGroup事件的傳遞過程

image

如上圖:
- 左圖:是ViewGroup巢狀View,不可滑動
- 右圖:也是ViewGroup(RecyclerView)巢狀View,可以滑動

主要想對比的就是,當手指按住View層,此時View層有Selector顏色變化,此時手指移動出View層的範圍,此時Touch事件的變化。

一,左圖 日誌分析

1) 點選日誌

TouchNormalActivity[dispatchTouchEvent, 29]: ACTION_DOWN
NormalViewGroup[dispatchTouchEvent, 49]: ACTION_DOWN
NormalViewGroup[onInterceptTouchEvent, 75]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN
TouchNormalActivity[dispatchTouchEvent, 37]: ACTION_UP
NormalViewGroup[dispatchTouchEvent, 56]: ACTION_UP
NormalViewGroup[onInterceptTouchEvent, 83]: ACTION_UP
NormalView[dispatchTouchEvent, 91]: ACTION_UP
NormalView2[onTouch, 64]: ACTION_UP
NormalView[onTouchEvent, 117]: ACTION_UP
NormalView$1[onClick, 49]: onClick: ======

此時的日誌就是一個正常的最內層View消費點選事件的日誌。我們能看到onClick的列印。
如果對這個不太明白的,可以移步上一篇文章加深理解

2) 點選-移動日誌

TouchNormalActivity[dispatchTouchEvent, 29]: ACTION_DOWN
NormalViewGroup[dispatchTouchEvent, 49]: ACTION_DOWN
NormalViewGroup[onInterceptTouchEvent, 75]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN

TouchNormalActivity[dispatchTouchEvent, 33]: ACTION_MOVE
NormalViewGroup[dispatchTouchEvent, 53]: ACTION_MOVE
NormalViewGroup[onInterceptTouchEvent, 79]: ACTION_MOVE
NormalView[dispatchTouchEvent, 88]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 114]: ACTION_MOVE

TouchNormalActivity[dispatchTouchEvent, 37]: ACTION_UP
NormalViewGroup[dispatchTouchEvent, 56]: ACTION_UP
NormalViewGroup[onInterceptTouchEvent, 83]: ACTION_UP
NormalView[dispatchTouchEvent, 91]: ACTION_UP
NormalView$2[onTouch, 64]: ACTION_UP
NormalView[onTouchEvent, 117]: ACTION_UP

首先描述一下手指的操作,點選View層,此時View由藍色變為紅色,然後移動手指到View層外面,View由紅色變為藍色。擡起手指。

這個過程的日誌,主要分為DOWN直接到內層,然後MOVE也是到內層,此時無論手指MOVE到View內層或者外層,MOVE的日誌是沒有變化的,而UP就是一個從外到內告訴你Touch事件分發完畢的過程。當然了最終的onClick是沒有執行的。

分析:首先當你手指點選螢幕,雖然是從外到內傳遞點選事件,但是事件的消費的反饋確實從內到外,ViewGroup回去遍歷內部View,然後決定哪個View消費到事件。當確定到點選到的View的時候就會呼叫該View內部的事件分發機制,同時如果該View設定有Selector就會進行相應的Selector變化。當手指MOVE到View外面的時候,ViewGroup在dispatch的時候回去再次遍歷計算。雖然此時的down是在View層的,但是焦點已經移動到VIEW層外面了。所以他的Selector效果消失。

但是因為DOWN層已經到View層了。所以此時的MOVE還是會傳遞到VIEW層。並沒有因為手指滑動出VIEW外面而導致時間傳遞不到VIEW層。但是onclick去沒有執行,是因為在VIEW層的onTouchEvent裡面。回去判斷當前手指的位置去呼叫performClick()方法。而這個方法內部就是回撥的onClickListener。所以我們看到View層的UP執行了。但是onClick卻沒有執行。

二,右圖 日誌分析

1) 點選日誌

點選recyclerView的Item此時的日誌和左圖-1中的日誌是一樣的。分析也是一樣的。

2) 點選-移動日誌

TouchRecyclerActivity[dispatchTouchEvent, 62]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN

TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 88]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 114]: ACTION_MOVE

TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 95]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 121]: ACTION_CANCEL

TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 92]: ACTION_MOVE

TouchRecyclerActivity[dispatchTouchEvent, 70]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 42]: ACTION_UP
NormalRecyclerView[onTouchEvent, 95]: ACTION_UP

從上面日誌可以看出,看出主要分為五個部分:

  1. DOWN從外到Item的View層
  2. MOVE在Item範圍內移動
  3. MOVE-CANCEL移動出item範圍的一個變化
  4. MOVE在外部ViewGroup的MOVE
  5. UP事件

從日誌我們可以看出來跟左圖的移動日誌相比,1,2是一樣的,後面的3,4,5都發生了變化。

檢視上的區別:首先左圖的DOWN-MOVE是檢視都沒有動,手指在動,手指移動出了VIEW的範圍。右圖,手指按壓當前item,移動,item跟著移動,其實手指一直是在View上的。這是他們的區別。

日誌上的區別:日誌上的區別主要體現在3,4,5上。

分析:雖然手指還在當前View上,但是item在螢幕上的位置是沒有變的。當手指移動出當前item所在的範圍的時候,首先是Selector的效果消失了。其次MOVE的日誌發生了變化,View層的dispatchTouchEvent和onTouchEvent都接收到了ACITON_CANCEL訊息,說明此時的外部VIEW取消了內層VIEW的消費事件的能力。此時的內層View,即為item已經沒有onTouchEvent的能力。我們知道MOVE進入的層級和那一層處理訊息是一致的。所以反應在4中,發現MOVE事件已經不能深入到item的View層了。在外部的ViewGroup的onTouchEvent處理了。此時我也發現了一個現象就是此時的日誌和我們在ViewGroup層攔截MOVE事件的日誌是一樣的。有興趣的同學可以去可能是最詳細的Android點選事件處理詳解對比一下。唯一的一點區別是,在ViewGroup層攔截以後,因為ViewGroup沒有做任何處理,外層的Activity的onTouchEvent也能接收到MOVE,但是這裡recyclerView沒有。說明recyclerView在這裡做了處理。

最後的UP因為做了攔截,所以UP只能傳遞到外部的recyclerView層。item層的onClick當然就不執行了。


專案原始碼:
https://github.com/wangxp423/ViewExercise

在首頁的Touch事件研究中。