用户登录
用户注册

分享至

android基础事件流程分析

  • 作者: 联合国灭猫灭狗小组长
  • 来源: 51数据库
  • 2021-08-19
建立嵌套的布局xml
 <com.g.grefresh.ViewGroup1
        android:id="@+id/parent1"
        android:orientation="vertical"
        android:layout_width="300dp"
        android:background="@android:color/holo_orange_dark"
        android:layout_height="300dp">
        <com.g.grefresh.ViewGroup2
            android:id="@+id/parent2"
            android:orientation="vertical"
            android:background="@color/colorPrimaryDark"
            android:layout_width="200dp"
            android:layout_height="200dp">
           <com.g.grefresh.View1
               android:id="@+id/view1"
               android:background="@android:color/white"
               android:layout_width="50dp"
               android:layout_height="50dp">

           </com.g.grefresh.View1>
        </com.g.grefresh.ViewGroup2>
    </com.g.grefresh.ViewGroup1>

布局分析:
最顶级的父布局 ViewGroup1。
ViewGroup1 的子布局ViewGroup2.
ViewGroup2拥有子View View1.

他们的事件代码如下:

ViewGroup1

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        var result = super.onTouchEvent(event)
        Log.e("gacmy","ViewGroup1 onTouchEvent:${result}")
        return result
    }

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
        return result
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.onInterceptTouchEvent(ev)
        Log.e("gacmy","ViewGroup1 onInterceptTouchEvent:${result}")
        return result
    }

ViewGroup2

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        var result = super.onTouchEvent(event)
        Log.e("gacmy","ViewGroup2 onTouchEvent:${result}")
        return result
    }
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        Log.e("gacmy","ViewGroup2 dispatchTouchEvent:${result}")
        return result
    }

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.onInterceptTouchEvent(ev)
        Log.e("gacmy","ViewGroup2 onInterceptTouchEvent:${result}")
        return result
    }

View1

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        var result = super.onTouchEvent(event)
        Log.e("gacmy","View1 onTouchEvent:${result}")
        return result
    }

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        Log.e("gacmy","View1 dispatchTouchEvent:${result}")
        return result
    }

触发单击事件的日志打印情况:

log1:E/gacmy: ViewGroup1 onInterceptTouchEvent:false
log2:E/gacmy: ViewGroup2 onInterceptTouchEvent:false
log3:E/gacmy: View1 onTouchEvent:false
log4:E/gacmy: View1 dispatchTouchEvent:false
log5:E/gacmy: ViewGroup2 onTouchEvent:false
log6:E/gacmy: ViewGroup2 dispatchTouchEvent:false
log7:E/gacmy: ViewGroup1 onTouchEvent:false
log8:E/gacmy: ViewGroup1 dispatchTouchEvent:false

下面分析日志为什么这样打印

  1. 首先我们假设ViewGroup1 是最顶级的事件来源
  2. 我们需要从ViewGruop1的dispatchTouchEvent 作为起始入口点分析.

ViewGroup1 dispatchTouchEvent执行逻辑很简单

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
        return result
    }
    首先执行ViewGroup1的父类 ViewGroup.java 的dispatchTouchEvent
    这里需要注意的是 result = super.dispatchTouchEvent
    需要等待父类的调用完成,才能打印返回。
    打一个tag1 这里需要等待返回打印日志。

ViewGroup1的父类 ViewGroup dispatchTouchEvent执行逻辑分析

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        
       
            //第一次在ViewGroup1里产生的ACTION_DOWN事件
            //if语句一定成立
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {

                //设置 FLAG_DISALLOW_INTERCEPT 标志位 disallowIntercept 为true
                //这里没有设置所以是false
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

               
                if (!disallowIntercept) {
                    //ViewGroup1 没有设置 FLAG_DISALLOW_INTERCEPT 标记位所以会走到这里
                    //调用ViewGroup1 的onInterceptTouchEvent方法
                    //紧接着会调用ViewGroup1父类ViewGroup的onInterceptTouchEvent方法
                    //ViewGroup onInterceptTouchEvent 默认返回false
                    //ViewGroup1 获得父类的返回结果并且打印返回false
                    //log1 在这里打印 intercepted 返回false
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }


        ...
         //没有发送取消事件 并且intercepted = false 执行到这里
         if (!canceled && !intercepted) {
         
             //遍历ViewGroup1的子布局 它的里面只有ViewGroup2
             for (int i = childrenCount - 1; i >= 0; i--) {
                 
                 ...
                 //这里调用ViewGroup dispatchTransformedTouchEvent 方法
                 //传入ViewGroup1 子布局 ViewGroup2 的对象
                 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                           ....    
                               
                }


                 ...
             }

         
         }


        
    }
     //ViewGroup1 父类ViewGroup调用的方法
     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
            ...
            
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            //child 是ViewGroup2 对象不为空
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }
            //这里开始调用ViewGroup2 的dispatchTouchEvent
            handled = child.dispatchTouchEvent(transformedEvent);
        }

            
            
            ...
    }

开始进入ViewGroup2 执行逻辑
ViewGroup2 dispatchTouchEvent执行逻辑很简单

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        Log.e("gacmy","ViewGroup2 dispatchTouchEvent:${result}")
        return result
    }
    首先执行ViewGroup2的父类 ViewGroup.java 的dispatchTouchEvent
    这里需要注意的是 result = super.dispatchTouchEvent
    需要等待父类的调用完成,才能打印返回。
    打一个tag2 这里需要等待返回打印日志。

下面进入ViewGroup2的父类ViewGroup的执行逻辑

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        
       
            //第一次在ViewGroup2里产生的ACTION_DOWN事件
            //if语句一定成立
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {

                //设置 FLAG_DISALLOW_INTERCEPT 标志位 disallowIntercept 为true
                //这里没有设置所以是false
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

               
                if (!disallowIntercept) {
                    //ViewGroup2 没有设置 FLAG_DISALLOW_INTERCEPT 标记位所以会走到这里
                    //调用ViewGroup2 的onInterceptTouchEvent方法
                    //紧接着会调用ViewGroup2父类ViewGroup的onInterceptTouchEvent方法
                    //ViewGroup onInterceptTouchEvent 默认返回false
                    //ViewGroup2 获得父类的返回结果并且打印返回false
                    //log2 在这里打印 intercepted 返回false
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }


        ...
         //没有发送取消事件 并且intercepted = false 执行到这里
         if (!canceled && !intercepted) {
         
             //遍历ViewGroup2的子布局 它的里面只有View1
             for (int i = childrenCount - 1; i >= 0; i--) {
                 
                 ...
                 //这里调用ViewGroup dispatchTransformedTouchEvent 方法
                 //传入ViewGroup2 子布局 View1 的对象
                 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                           ....    
                               
                }


                 ...
             }

         
         }


        
    }
     //ViewGroup2 父类ViewGroup调用的方法
     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
            ...
            
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            //child 是View1 对象不为空
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }
            //这里开始调用View1 的dispatchTouchEvent
            handled = child.dispatchTouchEvent(transformedEvent);
        }

            
            
            ...
    }

下面开始调用View1 dispatchTouchEvent方法

    //View1 首先调用自己父类View的 dispatchTouchEvent方法
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        //这里等待返回打印日志 打一个标记tag3
        Log.e("gacmy","View1 dispatchTouchEvent:${result}")
        return result
    }
    
    //View.java dispatchTouchEvent方法的调用
    public boolean dispatchTouchEvent(MotionEvent event) {
        ...
        if (onFilterTouchEventForSecurity(event)) {
            //如果EABLED 而且scrollbar正在被滑动 不会触发onTouchEvent事件
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            
            //onTouchListener 不等与null
            //而且onTouch返回true
            //就不会调用onTouchEvent事件
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            //scrollbar 没有滑动 而且 没有设置onTouchListener就会调用
            //onTouchEvent onTouchEvent返回true 结果返回true
            //onTouchEvent 返回false结果返回false
            //在这里View 调用 自己子类View1的onTouchEvent方法
            //等待子类View1的结果返回
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }


        
        ...
        return result
    }
    
    //View1 onTouchEvent方法调用
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        //首先调用父类的View.java 的onTouchEvent方法
        var result = super.onTouchEvent(event)
        //接着等待父类onTouchEvent方法的返回 View.java clickable 属性默认是false 这里返回false
        //log3在这里调用
        Log.e("gacmy","View1 onTouchEvent:${result}")
        return result
    }
  1. 这里View1 onTouchEvent返回false之后,就会返回到父类的方法,dispatchTouchEvent方法中.
  2. dispatchTouchEvent onTouchEvent返回false View1 父类 View.java dispatchTouchEvent也会返回结果false。
  3. 接下来返回到tag3,View1调用父类的dispatchTouchEvent 等待返回结果
  4. 打印日志 log4…
  5. view1 dispatchTouchEvent 是由ViewGroup2的dispatchTransformedTouchEvent调用
  6. ViewGroup2 获取到view1 dispatchTouchEvent返回结果false dispatchTransformedTouchEvent 也会跟着返回false
  7. ViewGroup2 dispatchTransformedTouchEvent方法是由ViewGroup2的父类dispatchTouchEvent调用

下面继续分析ViewGroup2 父类dispatchTouchEvent方法

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
      .....
      for (int i = childrenCount - 1; i >= 0; i--) {
        //来自View1的返回值 返回false ViewGroup2 只有一个View1 循环结束了
        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                              
            ....
        }
      }

      .....
      
      //ViewGroup2 子类View1 的dispatchTouchEvent返回false
      //这里mFirstTouchTarget == null 成立
      //mFirstTouchTarget 是事件序列的链表头,存储的是子View 和发送的事件类型,如果子View dispatchTouchEvent返回true 
      //接收了事件则会给这个链表添加新的头部
      //返回false 则不会为这个事件添加到链表里面,这里因为ViewGroup2只有一个子View,而且返回的fasle所以 mFirstTouchTarget是null
      if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                //这里caceled flase childView 也是null
                //会调用ViewGroup2 父类ViewGroup的dispatchEvent 
                //然后调用ViewGroup2 的onTouchEvent方法  //log5就在这系列调用中发生
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
      }
        
    }

上面的ViewGroup.java dispatchTouchEvent,是由ViewGroup2的dispatchTouchEvent调用的。所以返回到ViewGroup2 的dispatchTouchEvent 方法中 log6发生

ViewGroup2 的dispatchTouchEvent 是由ViewGroup1 的父类ViewGroup dispatchTouchEvent调用的。接下来分析 ViewGroup1 父类ViewGroup dispatchTouchEvent方法,类似于ViewGroup2的父类ViewGroup的dispatchTouchevent()方法的调用,这里mFirstTouchTarget == null 所以会调用,ViewGroup的 dispatchTouchEvent 方法,最终会调用ViewGroup1的onTouchEvent方法 log7发生

上面的返回后,就会返回到起点ViewGroup1的 dispatchTouchEvent中去

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var result = super.dispatchTouchEvent(ev)
        //这里返回fasle 调用日志方法
        Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
        //最终回调到我们事件的起点位置了
        return result
    }

这里View1发生点击事件,默认重写,事件传递的方法,只进行了一次ACTION_DOWN 事件的分发,后续滑动的ACTION_MOVE事件他们都收不到了?

  1. 这是因为我们的真正的事件起点不是ViewGroup1,而是Activity的DecorView,他也是一个FrameLayout布局,只需要分析它的dispatchTouchEvent代码就可了,也就是ViewGroup的dispatchTouchEvent

下面ViewGroup默认是真正的根布局的父类

    public boolean dispatchTouchEvent(MotionEvent ev) {
    
        ...
           //根布局获得ViewGroup1的返回false之后
           //根布局再产生MOVE事件的话 
           //actionMasked == MotionEvent.ACTION_DOWN 不成立
           //mFirstTouchTarget != null 也不成立 子View dispatchTouchEvent 因为返回false之后
           //mFirstTouchTarget 没有添加任何节点 导致这个条件也不成立
           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
                //后面不会再进行 循环调用子View的dispatchTouchEvent方法了
                //事件不会再传递下去
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }


    }
软件
前端设计
程序设计
Java相关