Android事件分發

// 點擊事件產生後,會直接調用dispatchTouchEvent分發方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    //代表是否消耗事件
    boolean consume = false;

    if (onInterceptTouchEvent(ev)) {
        //如果onInterceptTouchEvent()返回true則代表當前View攔截了點擊事件
        //則該點擊事件則會交給當前View進行處理
        //即調用onTouchEvent ()方法去處理點擊事件
        consume = onTouchEvent (ev) ;
    } else {
        //如果onInterceptTouchEvent()返回false則代表當前View不攔截點擊事件
        //則該點擊事件則會繼續傳遞給它的子元素
        //子元素的dispatchTouchEvent()就會被調用,重複上述過程
        //直到點擊事件被最終處理爲止
        consume = child.dispatchTouchEvent (ev) ;
    }
    return consume;
}

總結

1、事件分發時由activity->viewgroup->view,依次向下傳遞,消費事件後,且事件不會往下傳遞,後續事件會繼續分發到該view

2、事件不被消費,且事件停止傳遞,將事件回傳給父控件的onTouchEvent處理,當前view仍然接受此事件的其他事件
3、攔截事件後,事件不會往下傳遞,自己處理事件,即執行自己的onTouchEvent();同事件列的其他事件都由該view處理;在同一個事件列中的onInterceptTouchEvent()該方法不會再次被調用。

4、不攔截事件,事件會繼續往下傳遞,事件傳遞到子view  ,後續事件序列讓其處理

5、自己處理事件onToutchEvent(),事件不會繼續傳遞,後續事件序列讓其處理

6、不處理事件,將事件往上傳遞給父控件的onToutchEvent()處理,當前view不再接受此事件的其他事件

 

ViewGroup的dispatchTouchEvent()源碼分析

// 發生ACTION_DOWN事件或者已經發生過ACTION_DOWN,並且將mFirstTouchTarget賦值,才進入此區域,主要功能是攔截器
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
    //disallowIntercept:是否禁用事件攔截的功能(默認是false),即不禁用
    //可以在子View通過調用requestDisallowInterceptTouchEvent方法對這個值進行修改,不讓該View攔截事件
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    //默認情況下會進入該方法
    if (!disallowIntercept) {
        //調用攔截方法
        intercepted = onInterceptTouchEvent(ev); 
        ev.setAction(action);
    } else {
        intercepted = false;
    }
} else {
    // 當沒有觸摸targets,且不是down事件時,開始持續攔截觸摸。
    intercepted = true;
}

如果遍歷所有子View或ViewGroup,都沒有消費事件。ViewGroup會自己處理事件

如果子元素的dispatchTouchEvent()方法返回true,那麼mFirstTouchTarget就會被賦值,同時跳出for循環

這一段的內容主要是爲判斷是否攔截。如果當前事件的MotionEvent.ACTION_DOWN,則進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果mFirstTouchTarget != null,即已經發生過MotionEvent.ACTION_DOWN,並且該事件已經有ViewGroup的子View進行處理了,那麼也進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果不是以上兩種情況,即已經是MOVE或UP事件了,並且之前的事件沒有對象進行處理,則設置成true,開始攔截接下來的所有事件。 這也就解釋瞭如果子View的onTouchEvent()方法返回false,那麼接下來的一些列事件都不會交給他處理。如果VieGroup的onInterceptTouchEvent()第一次執行爲true,則mFirstTouchTarget = null,則也會使得接下來不會調用onInterceptTouchEvent(),直接將攔截設置爲true

addTouchTarget(child, idBitsToAssign);內部完成mFirstTouchTarget被賦值。如果mFirstTouchTarget爲空,將會讓ViewGroup默認攔截所有操作

/* 從最底層的父視圖開始遍歷,
   ** 找尋newTouchTarget,即上面的mFirstTouchTarget
   ** 如果已經存在找尋newTouchTarget,說明正在接收觸摸事件,則跳出循環。
    */
for (int i = childrenCount - 1; i >= 0; i--) {
  final int childIndex = customOrder
    ? getChildDrawingOrder(childrenCount, i) : i;
  final View child = (preorderedList == null)
    ? children[childIndex] : preorderedList.get(childIndex);

  // 如果當前視圖無法獲取用戶焦點,則跳過本次循環
  if (childWithAccessibilityFocus != null) {
     if (childWithAccessibilityFocus != child) {
        continue;
     }
     childWithAccessibilityFocus = null;
     i = childrenCount - 1;
  }
  //如果view不可見,或者觸摸的座標點不在view的範圍內,則跳過本次循環
  if (!canViewReceivePointerEvents(child) 
      || !isTransformedTouchPointInView(x, y, child, null)) {
    ev.setTargetAccessibilityFocus(false);
    continue;
    }

   newTouchTarget = getTouchTarget(child);
   // 已經開始接收觸摸事件,並退出整個循環。
   if (newTouchTarget != null) {
       newTouchTarget.pointerIdBits |= idBitsToAssign;
       break;
    }

    //重置取消或擡起標誌位
    //如果觸摸位置在child的區域內,則把事件分發給子View或ViewGroup
    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
        // 獲取TouchDown的時間點
        mLastTouchDownTime = ev.getDownTime();
        // 獲取TouchDown的Index
        if (preorderedList != null) {
           for (int j = 0; j < childrenCount; j++) {
               if (children[childIndex] == mChildren[j]) {
                    mLastTouchDownIndex = j;
                    break;
                }
           }
         } else {
                 mLastTouchDownIndex = childIndex;
                }

      //獲取TouchDown的x,y座標
      mLastTouchDownX = ev.getX();
      mLastTouchDownY = ev.getY();
      //添加TouchTarget,則mFirstTouchTarget != null。
      newTouchTarget = addTouchTarget(child, idBitsToAssign);
      //表示以及分發給NewTouchTarget
      alreadyDispatchedToNewTouchTarget = true;
      break;
}

詳細查看view事件分發

onTouch->onTouchEvent->onClick優先執行順序

view事件分發僞代碼

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