天天看點

【Android面試】View的事件分發

目錄

      • 分發的事件
      • View繼承關系
      • 事件分發處理流程
      • 事件分發
        • 頂級View對點選事件的分發過程
      • 事件處理
        • 事件處理總結

分發的事件

首先事件分發主要分為

【Android面試】View的事件分發

事件分發發生在ViewGroup中的dispatchTouchEvent中

action_move會觸發多次

View繼承關系

【Android面試】View的事件分發

view中dispatchTouchEvent用來處理事件

viewgroup中dispatchTouchEvent用來分發事件,不處理事件,viewgroup直接交給view去處理事件

事件分發處理流程

【Android面試】View的事件分發

viewgroup先走分發流程,再走處理流程

view隻走處理流程

【Android面試】View的事件分發

點選事件的整個分發流程大概:

首先點選事件,壓到螢幕後,觸發觸屏,導緻驅動,再到核心這一塊,然後再慢慢的到安卓底層,安卓底層再到源碼的話,安卓是直接先到activity的dispatchTouchEvent

【Android面試】View的事件分發

然後通過getWindow(window是一個抽象類,他隻有一個實作類就是PhoneWindow)調用PhoneWindow的superDispatchTouchEvent方法

【Android面試】View的事件分發

然後會調用DecorView的superDispatchTouchEvent

【Android面試】View的事件分發

然後調用父類的dispatchTouchEvent方法,這裡父類是FrameLayout,但其實是調用的viewgroup的dispatchTouchEvent方法

【Android面試】View的事件分發

我們的activity,以及viewGroup容器,都是通過viewGroup的dispatchTouchEvent來進行分發的,然後再由view.dispatchTouchEvent進行處理

注意:

分發是從底層的view一層一層分發

【Android面試】View的事件分發

處理是從頂層的view一層一層往上處理的

【Android面試】View的事件分發

處理事件最先處理的是我們最先看到的容器,而分發流程正好相反

事件分發

現在來梳理一下事件分發

我們其實可以想象我們的螢幕是一個立體坐标系

【Android面試】View的事件分發

首先點選事件産生時,先進入的是activity

activity的dispatchTouchEvent

【Android面試】View的事件分發

然後交給PhoneWindow的superDispatchTouchEvent

【Android面試】View的事件分發

然後再到DecorView的superDispatchTouchEvent

【Android面試】View的事件分發

然後調用父類的dispatchTouchEvent

DecorView是繼承FrameLayout的,但是FrameLayout沒有重寫dispatchTouchEvent,

【Android面試】View的事件分發

是以就到了我們的viewgroup的dispatchTouchEvent

【Android面試】View的事件分發

而viewgroup是重寫了父類的dispatchTouchEvent,是以可以用來進行事件分發,而事件處理是交給了父類,也就是view

super.dispatchTouchEvent -->view.dispatchTouchEvent

繼續回到事件分發

頂級View對點選事件的分發過程

點選事件到達頂級View(一般是ViewGroup)以後,會調用ViewGroup的dispatchTouchEvent方法,主要邏輯是這樣的,

如果頂級ViewGroup攔截事件也就是onInterceptTouchEvent傳回ture,那麼事件就交給ViewGroup處理,

(處理:這時如果ViewGroup的setOnTouchListener被調用的話,則OnTouch會被調用,否則OntouchEvent會被調用,也就是說都調用的情況下,onTouch會屏蔽掉onTouchEvent。在onTouchEvent中,如果設定了setOnClickListener,那麼onClick會被調用,如果onTouch傳回ture,那麼不會調用onTouchEvent,也就不會調用onClick)

如果頂級ViewGroup不攔截事件,那麼事件會傳遞給它所在的點選事件鍊上的子View,這時子View的dispatchTouchEvent會被調用,到此為止,事件已經從頂級View傳遞給了下一層View,接下來的傳遞過程和頂級View是一緻的。如此循環就完成了整個事件的分發。

事件處理

我們先建立一個Button,然後通過點選事件調用button的onClick和onTouch方法

如下:

【Android面試】View的事件分發

在這裡提出三個問題:

1.onTouch的傳回值 有什麼用?

2.onClick和onTouch哪個會先調用?

3.在哪裡調用?

event對應事件
ACTION_DOWN             = 0;//按下
ACTION_UP               = 1;//離開
ACTION_MOVE             = 2;//滑動
ACTION_CANCEL           = 3;//事件被上層攔截
           

如果onTouch傳回ture,會出現什麼情況?onClick和onTouch方法中的日志都會列印嗎?

答案如下:隻有onTouch方法中log會列印,

【Android面試】View的事件分發

如果onTouch傳回false呢?

【Android面試】View的事件分發

這裡是onTouch和onClick都會執行,而且 注意是onTouch先執行,

—>可見onTouch的傳回值決定oclick是否執行

我們的事件處理在view.dispatchTouchEvent裡面,主要是在這段代碼裡執行

【Android面試】View的事件分發

最外層的if判斷點選事件是否安全,一般來說我們的點選事件都是安全的。

然後判斷mListenerInfo是否為空。這裡我們需要看到前面的

setOnClickListener或者setOnTouchListener方法裡面,

【Android面試】View的事件分發

再看到getListenerInfo裡面

【Android面試】View的事件分發

這裡有一個單例模式,如果mListenerInfo不為空那麼直接傳回,否則建立一個mListenerInfo。

是以mListenerInfo肯定不為空。

【Android面試】View的事件分發

再看到li.mOnTouchListener != null這個判斷。

這個地方其實在

【Android面試】View的事件分發

調用方法的時候就已經new好了。是以這裡也是不為空的。

(mViewFlags & ENABLED_MASK) == ENABLED表示是否控件是可以點選的

然後就到了li.mOnTouchListener.onTouch(this, event),在這裡執行了onTouch。

如果onTouch傳回了false,result就會傳回false。如果onTouch傳回了true,result就會傳回true。

【Android面試】View的事件分發

然後在下面接着又有一個判斷,這裡的意思是result為false的話onTouchEvent就會執行,為true就不會執行

是以我們可以推測onclick方法在onTouchEvent方法裡面執行

進一步推測onTouchEvent方法不一定是執行的。當onTouch傳回ture,result傳回ture的時候就不會。、

再回到onclick的執行,我們可以發現log日志列印,onclick是在ACTION_UP 之後執行的,是以我們可以看到switch判斷下

case MotionEvent.ACTION_UP:中的

【Android面試】View的事件分發

看到這個PerformClick方法

【Android面試】View的事件分發

然後進入performClickInternal方法

【Android面試】View的事件分發

繼續performClick方法

【Android面試】View的事件分發

诶,有沒有一種撥開雲霧見光明的感覺

playSoundEffect(SoundEffectConstants.CLICK);//對點選聲音的處理
           

事件處理總結

總結順序就是

dispatchTouchEvent —>onTouch —> onTouchEvent —> onClick

如果onTouch傳回true,就不會繼續了