Android ViewGroup事件分发机制

接着上一篇View的事件分发机制,我接着在说一说ViewGroup的事件分发机制

1.接着来看ViewGroup的事件分发机制,同上一篇,先罗列ViewGroup中主要的方法

ViewGroup的事件分发主要有三个方法
1. dispatchTouchEcent(); 用来分派事件
2. onInterceptTouchEvent ();用来拦截事件
3. onTouchEvent ();用来处理事件

我整理出来的ViewGroup事件分发源码,这里只展示一部分关键代码,其他的可以自行去看源码
    private TouchTarget mFirstTouchTarget;
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        if (actionMasked == MotionEvent.ACTION_DOWN){
                //清除Target    只要知道mFirstTouchTarget = null;
                cancelAndClearTouchTargets(ev);
        }
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                //相当于调用getParent().requestDisallowInterceptTouchEvent();方法请求父类是否拦截
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); 
                } else {
                    intercepted = false;
                }
            } else {   
                intercepted = true;
       }
       TouchTarget newTouchTarget = null;
       if (!canceled && !intercepted) {
            final View[] children = mChildren;
            for (int i = childrenCount - 1; i >= 0; i--) {
                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                     //给mFirstTouchTarget赋值
                     newTouchTarget = addTouchTarget(child, idBitsToAssign);
                }
            }
        }
        if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
         }
    }

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            return handled;
        }
}

ViewGroup事件分发流程图
ViewGroup事件分发流程图
源码分析

当一个事件传到ViewGroup中的时候,会先看到事件拦截的逻辑,先看下面代码

            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }
  1. 首先当一个事件进来的时候,会先判断当前事件是否是down或着mFirstTouchTarget是否为null(当我们第一次进到ViewGroup.dispatchTouchEvent的时候mFirstTouchTarget会为空的),如果当前事件为down或者mFirstTouchTarget为null的时候就会调用ViewGroup.onInterceptTouchEvent方法,接着会将onInterceptTouchEvent的返回进行记录
  2. 可能有小伙伴会问,disallowIntercept这个值是什么东西,不知道大家有没有用过getParent().requestDisallowInterceptTouchEvent(boolean);方法,当我们给这个方法设置什么,disallowIntercept就会是什么值,所以我们在事件拦截的时候,可以在其子View里面调用该方法进行事件拦截

当走完事件拦截方法之后,程序会拿着onInterceptTouchEvent方法的结果,也就是intercepted的值进行下面的判断

    if (!canceled && !intercepted) {}

当intercepted为false也就是不拦截的时候,就会遍历子元素,并将事件向下分发交给子元素进行处理。

     final View[] children = mChildren;
     for (int i = childrenCount - 1; i >= 0; i--) {
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                  //给mFirstTouchTarget赋值
                  newTouchTarget = addTouchTarget(child, idBitsToAssign);
           }
     }

可以看到当在遍历子孩子的时候会调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)方法,,而在dispatchTransformedTouchEvent方法中我们会发现下面代码,通过下面代码会发现,当child不为空的时候他就会直接调用子元素的dispatchTouchEvent方法,这样事件就交由子元素处理了,从而完成一轮分发。

   if (child == null) {
         handled = super.dispatchTouchEvent(event);
   } else {
          handled = child.dispatchTouchEvent(event);
   }

当子View.dispatchTouchEvent返回为true时,就会调用addTouchTarget(child, idBitsToAssign)方法,该方法就是在给mFirstTouchTarget赋值。
当子View.dispatchTouchEvent返回为false时,就不会调用addTouchTarget(child, idBitsToAssign)方法,故mFirstTouchTarget为null

那么mFirstTouchTarget为null时会出现什么情况呢,继续向下看,会看到下面的代码,注意这里的view传的是null,也就是说会调用super.dispatchTouchEvent(event)代码,super.dispatchTouchEvent(event)是什么呢?他就是我们自己的dispatchTouchEcent方法。也就是事件将我们自己去处理

        if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
         }

到这里ViewGroup的事件分发机制就分析完了

在上一篇View的事件分发机制中我,有提过两个问题
  1. 重写dispatchTouchEvent、onTouchEvent、设置onTouchEventListener、设置onClickListener但是dispatchTouchEvent返回值不是super而是false的执行效果
  2. 重写dispatchTouchEvent、onTouchEvent、设置onTouchEventListener、但是不设置onClickListener的执行效果。

其实这两个原理都是相同的,只是运行出来的效果不一样

  1. 第一个问题运行效果是
    ViewGroup.dispatchTouchEvent.down -> ViewGroup.onInterceptTouchEvent.down -> View.dispatchTouchEvent.down-> ViewGroup.onTouchEvent.down

我们可以看到当View. dispatchTouchEvent返回false的时候ViewGroup.dispatchTransformedTouchEvent方法也就返回false故mFirstTouchTarget为null

  1. 第二个问题运行效果是
    ViewGroup.dispatchTouchEvent.down -> ViewGroup.onInterceptTouchEvent.down -> View.dispatchTouchEvent.down-> View.onTouchEventListener.down -> View.onTouchEcent -> ViewGroup.onTouchEvent.down

当View中没有设置onClickListener的时候,也就是说View. dispatchTouchEvent返回false,导致ViewGroup.dispatchTransformedTouchEvent方法也就返回false故mFirstTouchTarget为null

所以上面两个问题最终原因都是因为mFirstTouchTarget为null,当mFirstTouchTarget为null时会调用super.dispatchTouchEvent(event)代码,也就是事件都会交由我们ViewGroup自己去处理,故最后都会调用ViewGroup的onTouchEvent方法。

你可能感兴趣的:(Android ViewGroup事件分发机制)