Android 分發事件機制

前言

當Android系統捕獲到用戶的各種輸入事件後,如何準確地傳遞給真正需要這個事件的控件呢?Android給我們提供了一整套完善的事件傳遞、處理機制,來幫助開發者完成準確的事件分配和處理。

基礎知識

  1. MotionEvent
    • 所有觸摸事件都被封裝成MotionEvent對象,包括Touch的位置、時間、歷史記錄以及第幾個手指(多指觸摸)等。
    • 一次完整的MotionEvent事件,是從用戶觸摸屏幕到離開屏幕。整個過程的動作可如示:ACTION_DOWNACTION_DOWN(1次)-> ACTION_MOVE(N次)- > ACTION_UP(1次)
    • 多點觸摸,每一個觸摸點Pointer會有一個id和index。對於多指操作,通過pointerindex來獲取指定Pointer的觸屏位置。比如,對於單點操作時獲取x座標通過getX(),而多點操作獲取x座標通過getX(pointerindex)
  2. View
    • View是所有視圖對象的父類,實現了動畫相關的接口Drawable.Callback, 按鍵相關的接口KeyEvent.Callback, 交互相關的接口AccessibilityEventSource。比如Button繼承自View。
    • TouchEvent事件處理相關的方法:
      dispatchTouchEvent(MotionEvent event)
      onTouchEvent(MotionEvent event)
  3. ViewGroup
    • ViewGroup,是一個abstract類,一組View的集合,可以包含View和ViewGroup,是所有佈局的父類或間接父類。繼承了View,實現了ViewParent(用於與父視圖交互的接口), ViewManager(用於添加、刪除、更新子視圖到Activity的接口)。比如常用的LinearLayout,RelativeLayout都是繼承自ViewGroup。
    • TouchEvent事件處理相關的方法:
      dispatchTouchEvent(MotionEvent event)
      onInterceptTouchEvent(MotionEvent ev)
      onTouchEvent(MotionEvent event)
  4. Activity
    • Activity是Android四大基本組件之一,這裏只介紹Activity與touch相關的方法。當手指觸摸到屏幕時,屏幕硬件一行行不斷地掃描每個像素點,獲取到觸摸事件後,從底層產生中斷上報。再通過native層調用Java層InputEventReceiver中的dispatchInputEvent方法。經過層層調用,交由Activity的dispatchTouchEvent方法來處理。
    • TouchEvent事件處理相關的方法:
      dispatchTouchEvent(MotionEvent event)
      onTouchEvent(MotionEvent event)

流程圖

當用戶觸摸屏幕時會產生一系列MotionEvent,那麼Activity,ViewGroup,View之間是如何處理呢?這裏通過一張圖來從全局性描述整個流程

這裏寫圖片描述

結論

流程圖總結

  • onInterceptTouchEvent返回值true表示事件攔截, onTouch/onTouchEvent 返回值true表示事件消費。

  • 觸摸事件先交由Activity.dispatchTouchEvent。再一層層往下分發,當中間的ViewGroup都不攔截時,進入最底層的View後,開始由最底層的OnTouchEvent來處理,如果一直不消費,則最後返回到Activity.OnTouchEvent。

  • ViewGroup纔有onInterceptTouchEvent攔截方法。在分發過程中,中間任何一層ViewGroup都可以直接攔截,則不再往下分發,而是交由發生攔截操作的ViewGroup的OnTouchEvent來處理。

  • 子View可調用requestDisallowInterceptTouchEvent方法,來設置disallowIntercept=true,從而阻止父ViewGroup的onInterceptTouchEvent攔截操作。

  • OnTouchEvent由下往上冒泡時,當中間任何一層的OnTouchEvent消費該事件,則不再往上傳遞,表示事件已處理。

  • 如果View沒有消費ACTION_DOWN事件,則之後的ACTION_MOVE等事件都不會再接收。

  • 只要View.onTouchEvent是可點擊或可長按,則消費該事件.

  • onTouch優先於onTouchEvent執行,上面流程圖中省略,onTouch的位置在onTouchEvent前面。當onTouch返回true,則不執行onTouchEvent,否則會執行onTouchEvent。onTouch只有View設置了OnTouchListener,且是enable的才執行該方法。

區分總結

  1. View觸摸事件傳遞機制:

    • 觸摸控件(View)首先執行dispatchTouchEvent方法。
    • 在dispatchTouchEvent方法中先執行onTouch方法,後執行onClick方法
    • 如果控件(View)的onTouch返回false或者mOnTouchListener爲null(控件沒有設置setOnTouchListener方法)或者控件不是enable的情況下會調運onTouchEvent,dispatchTouchEvent返回值與onTouchEvent返回一樣。
    • 如果控件不是enable的設置了onTouch方法也不會執行,只能通過重寫控件的onTouchEvent方法處理(上面已經處理分析了),dispatchTouchEvent返回值與onTouchEvent返回一樣。
    • 如果控件(View)是enable且onTouch返回true情況下,dispatchTouchEvent直接返回true,不會調用onTouchEvent方法。
    • 當dispatchTouchEvent在進行事件分發的時候,只有前一個action返回true,纔會觸發下一個action(也就是說dispatchTouchEvent返回true纔會進行下一次action派發)。
  2. ViewGroup觸摸事件傳遞機制:

    • Android事件派發是先傳遞到最頂級的ViewGroup,再由ViewGroup遞歸傳遞到View的。
    • 在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進行攔截,onInterceptTouchEvent方法返回true代表不允許事件繼續向子View傳遞,返回false代表不對事件進行攔截,默認返回false。
    • 子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件。
  3. Activity觸摸事件傳遞機制:

    • 首先會觸發Activity的dispatchTouchEvent方法
    • dispatchTouchEvent方法中如果是ACTION_DOWN的情況下會接着觸發onUserInteraction方法。
    • 接着在dispatchTouchEvent方法中會通過Activity的root View(id爲content的FrameLayout),實質是ViewGroup,通過super.dispatchTouchEvent把touchevent派發給各個activity的子view,也就是我們再Activity.onCreat方法setContentView時設置的view。
    • 若Activity下面的子view攔截了touchevent事件(返回true)則Activity.onTouchEvent方法就不會執行

最後

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章