zoukankan      html  css  js  c++  java
  • Android事件分发机制详解(2)----分析ViewGruop的事件分发

      首先,我们需要 知道什么是ViewGroup,它和普通的View有什么区别?

      ViewGroup就是一组View的集合,它包含很多子View和ViewGroup,是Android 所有布局的父类或间接父类.

      但ViewGroup也是一个View,只不过比起View,它可以包含子View和定义布局参数的功能.

      现在,通过一个Demo演示Android中ViewGroup的事件分发机制.

      首先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下 所示:

         

    1.   publicclassMyLayoutextendsLinearLayout{
    2.       publicMyLayout(Context context,AttributeSet attrs){
    3.       super(context, attrs);
    4.       }
    5.   }
     

      然后,打开主布局文件activity_main.xml,在其中加入我们自定义的布局:

    1.  xmlns:tools="http://schemas.android.com/tools"
    2.   android:id="@+id/my_layout"
    3.   android:layout_width="match_parent"
    4.   android:layout_height="match_parent"
    5.   android:orientation="vertical" >
    6.   
    7.   android:id="@+id/button1"
    8.   android:layout_width="match_parent"
    9.   android:layout_height="wrap_content"
    10.   android:text="Button1" />
    11.   
    12.   android:id="@+id/button2"
    13.   android:layout_width="match_parent"
    14.   android:layout_height="wrap_content"
    15.   android:text="Button2" />
     

      可以看到,我们在MyLayout中添加了两个按钮,接着在MainActivity中为这两个按钮和MyLayout都注册了监听事件:

         

    1.  myLayout.setOnTouchListener(newOnTouchListener(){
    2.   @Override
    3.       publicboolean onTouch(View v,MotionEvent event){
    4.           Log.d("TAG","myLayout on touch");
    5.       returnfalse;
    6.       }
    7.   });
    8.   button1.setOnClickListener(newOnClickListener(){
    9.   @Override
    10.       publicvoid onClick(View v){
    11.           Log.d("TAG","You clicked button1");
    12.       }
    13.   });
    14.   button2.setOnClickListener(newOnClickListener(){
    15.   @Override
    16.       publicvoid onClick(View v){
    17.           Log.d("TAG","You clicked button2");
    18.       }
    19.   });
     

      我们在MyLayout的onTouch方法,和Button1、Button2的onClick方法中都打印了一句话。现在运行一下项目,效果图如下所示:

      

      分别点击一下Button1、Button2和空白区域,打印结果如下所示:

      

      当你点击按钮时,MyLayout的Touch事件并不会被触发,而在点击除按钮之外的空白部分,却能触发MyLayot的Touch事件.

      难道事件是从View传递到ViewGroup中的?现在下结论还太早.

      查阅文档,发现ViewGroup中还有一个onInterceptTouchEvent()方法,

            

    1.   publicboolean onInterceptTouchEvent(MotionEvent ev){
    2.    returnfalse;
    3.   }
     

      看Intercept,知道这应该是一个拦截器,拦截Touch事件的.但默认确实对所有的Touch都不拦截.

      在MyLayout中重写一下这个方法,然后返回一个true.

         

    1.  publicclassMyLayoutextendsLinearLayout{
    2.   publicMyLayout(Context context,AttributeSet attrs){
    3.    super(context, attrs);
    4.   }
    5.   @Override
    6.   publicboolean onInterceptTouchEvent(MotionEvent ev){
    7.    returntrue;
    8.    }
    9.   }
     

     

      现在再次运行项目,然后分别Button1、Button2和空白区域,打印结果如下所示:

      

      你会发现,不管点击在哪里,永远都只会触发MyLayout的Touch事件.按钮的点击事件被完全屏蔽掉了.

      所以得出结论:Touch事件会先分发到ViewGroup中,再传递到View中.

      上篇有个结论,触摸任何控件,都会调用dispatchTouchEvent()方法.其实不太准确.

      实际情况是,当点击了某个控件,首先会调用所在布局ViewGroup的dispatchTouchEvent(),在方法里,找到子View,调用子VIew的dispatchTouchEvent.

      当点击了MyLayout时,就会调用MyLayout的dispatchTouchEvent方法,但是MyLayout并没有这个方法,直到父类Linearlayout的父类ViewGroup中才有.

      

      下面是ViewGroup中dispatchTouchEvent()的源码:

      

    1. public boolean dispatchTouchEvent(MotionEvent ev) {  
    2.     final int action = ev.getAction();  
    3.     final float xf = ev.getX();  
    4.     final float yf = ev.getY();  
    5.     final float scrolledXFloat = xf + mScrollX;  
    6.     final float scrolledYFloat = yf + mScrollY;  
    7.     final Rect frame = mTempRect;  
    8.     boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
    9.     if (action == MotionEvent.ACTION_DOWN) {  
    10.         if (mMotionTarget != null) {  
    11.             mMotionTarget = null;  
    12.         }  
    13.         if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
    14.             ev.setAction(MotionEvent.ACTION_DOWN);  
    15.             final int scrolledXInt = (int) scrolledXFloat;  
    16.             final int scrolledYInt = (int) scrolledYFloat;  
    17.             final View[] children = mChildren;  
    18.             final int count = mChildrenCount;  
    19.             for (int i = count - 1; i >= 0; i--) {  
    20.                 final View child = children[i];  
    21.                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
    22.                         || child.getAnimation() != null) {  
    23.                     child.getHitRect(frame);  
    24.                     if (frame.contains(scrolledXInt, scrolledYInt)) {  
    25.                         final float xc = scrolledXFloat - child.mLeft;  
    26.                         final float yc = scrolledYFloat - child.mTop;  
    27.                         ev.setLocation(xc, yc);  
    28.                         child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
    29.                         if (child.dispatchTouchEvent(ev))  {  
    30.                             mMotionTarget = child;  
    31.                             return true;  
    32.                         }  
    33.                     }  
    34.                 }  
    35.             }  
    36.         }  
    37.     }  
    38.     boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
    39.             (action == MotionEvent.ACTION_CANCEL);  
    40.     if (isUpOrCancel) {  
    41.         mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
    42.     }  
    43.     final View target = mMotionTarget;  
    44.     if (target == null) {  
    45.         ev.setLocation(xf, yf);  
    46.         if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
    47.             ev.setAction(MotionEvent.ACTION_CANCEL);  
    48.             mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
    49.         }  
    50.         return super.dispatchTouchEvent(ev);  
    51.     }  
    52.     if (!disallowIntercept && onInterceptTouchEvent(ev)) {  
    53.         final float xc = scrolledXFloat - (float) target.mLeft;  
    54.         final float yc = scrolledYFloat - (float) target.mTop;  
    55.         mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
    56.         ev.setAction(MotionEvent.ACTION_CANCEL);  
    57.         ev.setLocation(xc, yc);  
    58.         if (!target.dispatchTouchEvent(ev)) {  
    59.         }  
    60.         mMotionTarget = null;  
    61.         return true;  
    62.     }  
    63.     if (isUpOrCancel) {  
    64.         mMotionTarget = null;  
    65.     }  
    66.     final float xc = scrolledXFloat - (float) target.mLeft;  
    67.     final float yc = scrolledYFloat - (float) target.mTop;  
    68.     ev.setLocation(xc, yc);  
    69.     if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
    70.         ev.setAction(MotionEvent.ACTION_CANCEL);  
    71.         target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
    72.         mMotionTarget = null;  
    73.     }  
    74.     return target.dispatchTouchEvent(ev);  
    75. }  

      

      在第13行中,对onInterceptTouchEvent取反,因此如果在onInterceptTouchEvent中返回了false,会让表达式成立.

      结合之前的例子,在MyLayout重写了onInterceptTouchEvent,返回true,结果导致所有的按钮事件都被屏蔽.所以得出结论,按钮的点击处理事件,都是在13行中内部进行的.

      在上篇中,得出结论,调用View的dispatchTouchEvent方法都是有返回值的.如果一个控件是可以点击的,那么dispatchTouchEvent的返回值必定是true.

      因此如果按钮的点击事件得到执行,就会将MyLayout的Touch事件拦截掉.

      如果不是点击的按钮,而是空白区域,则31行一定不会返回true了,而是执行50行的return super.dispatchTouchEvent(ev);  这句代码会调用View中的dispatchTouchEvent方法.因为ViewGroup的父类就是View.因此MyLayout的Touch方法会得到执行.

      看一下整个ViewGroup事件分发过程的流程图:

      

      1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。

      2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

      3. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。





    qq3061280@163.com
  • 相关阅读:
    为自己的开篇
    软考程序员笔记
    centos php7 安装mysqli扩展心得
    php判断访问协议是否是https
    go语言新建多维map集合
    获取contenteditable区域光标所在位置信息
    ckeditor中 config.js等通过ckeditor.js引入文件手动修改方法
    Vue使用——v-for循环里面使用v-if判断显示数据
    数据库关联字段设置
    Spring Jpa 自动建表——时间字段设置
  • 原文地址:https://www.cnblogs.com/aibuli/p/1092a3fda6e16fac32fd342131dc48b4.html
Copyright © 2011-2022 走看看