美文网首页
Android ViewGroup事件分发机制

Android ViewGroup事件分发机制

作者: LogyKoala | 来源:发表于2019-03-12 20:05 被阅读0次

    接着上一篇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事件分发机制

          本文链接:https://www.haomeiwen.com/subject/ldunpqtx.html