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事件分发伪代码

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