欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android基础事件流程分析

程序员文章站 2022-03-04 16:48:45
建立嵌套的布局xml ...
建立嵌套的布局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;
            }


    }

本文地址:https://blog.csdn.net/gacmy/article/details/110291916

相关标签: aosp