天天看點

[知識點整理]Android事件傳遞機制

基本知識

  1. 每個事件都被封裝成為

    MotionEvent

    ,包含了Touch的位置,點按的數量(手指的數量),時間點等資訊
  2. 包含了使用者的目前動作,主要有以下幾種類型
    • ACTION_DOWN

    • ACTION_UP

    • ACTION_MOVE

    • ACTION_POINTER_DOWN

    • ACTION_POINTER_UP

    • ACTION_CANCEL

  3. Android事件響應涉及的主要方法有以下幾個:
    • dispatchTouchEvent()

      用于事件的分發,所有的事件都要通過此方法進行分發,決定是自己對事件進行消費還是交由子View處理.
    • onTouchEvent()

      主要用于事件的處理,傳回true表示消費目前事件.
    • onInterceptTouchEvent

      ViewGroup

      中獨有的方法,若傳回

      true

      表示攔截目前事件,交由自己的

      onTouchEvent()

      進行處理,傳回

      false

      表示不攔截
  4. 事件被消費的意思是

    dispatchTouchEvent()

    ,

    onTouchEvent()

    ,

    onInterceptTouchEvent()

    等方法傳回

    true

流程

事件由Activity的

dispatchTouchEvent()

開始,将事件傳遞給目前Activity的根View,事件開始自上而下進行傳遞,直至被消費.

事件傳遞至

View

dispatchTouchEvent()

時, 首先會判斷

OnTouchListener

是否存在,倘若存在者則由

OnTouchListener

進行消費,執行

onTouch()

,.若

onTouch()

未對事件進行消費,事件将繼續交由

onTouchEvent

處理,通過檢視源碼可知,View的

onClick

事件是在

onTouchEvent

ACTION_UP

中觸發的,是以,onTouch事件優先于

onClick

事件.

事件傳遞至

ViewGroup

時,調用

dispatchTouchEvent()

進行處理:

1. 檢查送否應該對事件進行攔截:

onInterceptTouchEvent()

,若為true,跳過2步驟

2. 按照子View添加順序的逆序,将事件依次分發給子View,若事件被前一個View消費了,将不再繼續分發

3. 如果2中沒有子View對事件進行消費或者子View的數量為零,事件的處理流程和View的處理流程一緻

若事件在自上而下的傳遞過程中一直沒有被消費,而且最底層的子View也沒有對其進行消費,事件會反向向上傳遞,此時,父

ViewGroup

可以對事件進行消費,若仍然沒有被消費的話,最後會回到Activity的

onTouchEvent

下面附上整個事件傳遞的流程圖

[知識點整理]Android事件傳遞機制

總結

  1. 事件總是由

    Activity

    dispatchTouchEvent

    開始向下進行傳遞
  2. 父View(

    ViewGroup

    )可将事件傳遞給子View,

    ViewGroup

    可将事件攔截,事件将停止向下傳遞(

    onInterceptTouchEvent

    中傳回

    true

    )
  3. 事件自上而下傳遞,直到被消費
  4. 如果View或者ViewGroup沒有消費

    ACTION_DOWN

    ,其他事件也不會被傳遞進來
  5. 所有未被消費的事件将會回傳至Activity的

    onTouchEvent()

    (事件的最後一環)
  6. OnTouchListener

    優先于

    onTouchEvent()

    對事件進行消費

技巧

對于底層的View來說,可以使用

getParent().requestDisallowInterceptTouchEvent(true)

來阻止父View攔截Touch事件.

實踐過程中,常用與解決嵌套滑動事件沖突的問題:如scrollView+listview方式,此時可重寫onTouchEvent判斷滑動位置.

當子View需要滑動時,在子view中使用

requestDisallowInterceptTouchEvent(true);

阻止父View攔截消費事件

當子view滑動到邊界才

requestDisallowInterceptTouchEvent(false);