View 的事件分發(fā)機制

一 點擊事件的傳遞規(guī)則

??當一個 MothinEvent 產(chǎn)生了以后,系統(tǒng)需要把這個事件傳遞給一個具體的 View,而這個傳遞過程就是分發(fā)過程,由三個方法共同完成:

  • dispatchTouchEvent(MotionEvent ev)
    ?用來進行事件的分發(fā)。如果事件能夠傳遞給當前 View ,那么此方法一定被調(diào)用,返回結(jié)果受當前 View 的 onTouchEvent下級的 dispatchTouchEvent 方法影響,表示是否消耗該事件。
  • onInterceptTouchEvent(MotionEvent event)
    ?在dispatchTouchEvent方法中調(diào)用,判斷是否攔截某個事件,如果 View 攔截了某個事件,那么在同一個事件序列中,此方法不會被再次調(diào)用,返回結(jié)果表示是否攔截該事件。
  • onTouchEvent(MotionEvent event)
    ?在 dispatchTouchEvent 方法中調(diào)用,用來處理點擊事件,返回結(jié)果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,當前 View 無法再次接收到該事件。

上述三者關系,如下偽代碼

public boolean dispatchTouchEvent(MotionEvent ev) {
      boolean consume = false;
       if (onInterceptTouchEvent(ev)){      //是否攔截
           consume = onTouchEvent(ev);    //攔截調(diào)用 onTouchEvent
       }else {
          consume = child.dispatchTouchEvent(ev);  //不攔截分發(fā)給子 View  
       }
       return consume;
   }

事件分發(fā)優(yōu)先級:
? onTouch > onTouchEvent > onClick;
事件分發(fā)過程
??點擊事件產(chǎn)生后,它的傳遞有如下順序:Activity -> Window -> View , 即事件總是先傳遞給 Activity,Activity 再傳遞給 Window ,最后 Window 再傳遞給頂級 View,頂級的 View 接收到事件后,就會按照事件分發(fā)機制去分發(fā)事件。
??如果一個 View 的 onTouchEvent 返回 false,那么他的父容器的 onTouchEvent 會調(diào)用,如果都不處理這個事件,那么最后會傳遞到Activity的 onTouchEvent 處理。

結(jié)論:

  • 1.事件序列:是手指接觸到屏幕那一刻起,到手指離開屏幕的那一刻起,這個過程從 down 事件開始,中間有數(shù)量不定的 move 事件,最終以 up 事件結(jié)束。(down -> move ...move->up)
    1. 正常情況一個事件序列只能被一個 View 攔截消耗,即同一個事件交給一個 View 處理。但可以通過一個 View 的onTouchEvent 強行傳遞給其他 View 處理。
    1. 某個 View 一旦決定攔截,這個事件序列只能交給它處理,他的 onInterceptTouchEvent()不再調(diào)用。
    1. 某個 View 一旦開始處理事件,如果它不消耗 ACTION_DOWN 事件(onTouchEvent 返回 false),他會將這個事件從新交給他的父元素處理,即父容器的 onTouchEvent 會被調(diào)用。一旦交給一個View處理,那么它就必須消耗掉,否則同一事件序列剩下的事件就不再交給它處理了。
    1. 如果 View 只消耗了 ACTION_DOWN 事件,那么這個點擊事件會消失,父容器的 onTouchEvent 并不會調(diào)用,并且當前的 View 可以持續(xù)收到后續(xù)的事件,最終這些消失的點擊事件會傳遞給 Activity 處理。
    1. ViewGroup 默認不攔截任何事件,Android 源碼中 ViewGroup 的 onInterceptTouchEvent 方法默認返回 false。
    1. View 的 onTouchEvent 默認都是消耗事件(返回true),除非它是不可點擊的 >(clickable 和 longClickable 同時為 false)
      View 的 longClickable屬性 :默認都為 false。
      clickable 屬性:要分情況討論,比如 Button 的 clickable 為 ture,TextView 的 clickable 屬性>為 false 。
    1. View 中沒有 onInterceptTouchEvent 方法,一旦有事件傳遞給它,那么它的 onTouchEvent 方法就會被調(diào)用。
    1. View 的 enable 屬性不影響 onTouchEvent 的默認返回值,假如 View 是 disable 狀態(tài)的,只要它的 clickable 和 longClickable 有一個為 true,那么它的 onTouchEvent 就返回 true
    1. onClick 會發(fā)生的前提是 View 是可點擊的,并且它收到了 down 和 up 的事件。
    1. 事件傳遞過程是由外向內(nèi)的,即事件總先傳遞給父元素,然后再由父元素分給子 View,通過 requestDisallowInterceptTouchEvent 方法可以在子元素中干預父元素的事件分發(fā)。(ACTION_DOWN 事件除外)

二 View 的繪制流程

? View 的繪制流程是從 ViewRoot 的 performTraversals 方法開始的,performTraversals 會依次調(diào)用 performMeasure ,performLayout,performDraw 三個方法分別完成頂級 View 的measure,layout,draw 這三大流程,
??其中在 performMeasure 中會調(diào)用 measure 方法,measure 方法會調(diào)用 onMeasure 方法,在 onMeasure 中會完成子元素進行 measure 過程,子元素也會重復父元素的這個過程,performLayout,performDraw 同理。

三 理解 MeasureSpec

?在測量過程中,系統(tǒng)會將 View 的 LayoutParams 根據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成對應的 MeasureSpec ,然后根據(jù)這個 MeasureSpec 測量出 View 的寬/高。

  • MeasureSpec 代表一個 32 位 int 值,高 2 位代表 SpecMode ,低 30 位代表 SpecSize。
    ??SpecMode:測量模式。
    ??SpecSize:某種測量模式下規(guī)格的大小。
  • MeasureSpec 將 SpecMode與SpecSize 打包成一個 int 值,避免過多的內(nèi)存分配,而 MeasureSpec 值可以解包成 SpecMode與SpecSize 。(MeasureSpec 這里指的 int 值)
  • SpecMode有三類:
    ??UNSPECIFIED(未指明):父容器不對 View 有任何限制,要多大給多大,這種情況一般用于系統(tǒng)內(nèi)部,表示一種測量狀態(tài)。
    ??EXACTLY(恰好的):父容器已經(jīng)檢測出 View 所需要的大小,這個時候 View 的最終大小就是 SpecSize 所指定的值,它對應 LayoutParams 中的 match_parent 和具體數(shù)值的兩種模式。
    ??AT_MOST(至多的):父容器指定了一個大小可用的 SpecSize ,View 的大小不能大于這個值,具體是什么值看 View 的具體實現(xiàn),它對應 LayoutParams 的 wrap_content。
    決定因素:值由子View的布局參數(shù)LayoutParams和父容器的MeasureSpec值共同決定。具體規(guī)則見下圖:
    普通 View 的 MeasureSpec 的創(chuàng)建規(guī)則.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容