30分钟搞清楚Android Touch事件分发机制

 更新时间:2016年03月02日 10:53:49   作者:阿冰的学习日记  
30分钟搞清楚Android Touch事件分发机制,Touch事件分发中只有两个主角:ViewGroup和View,想要深入学习的朋友可以参考本文

Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。

View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。

先分析ViewGroup的处理流程:首先得有个结构模型概念:ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:

当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。来个简单版的代码加深理解:
 

/**
   * ViewGroup
   * @param ev
   * @return
   */
  public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他处理,在此不管
    View[] views=getChildView();
    for(int i=0;i<views.length;i++){
      //判断下Touch到屏幕上的点在该子View上面 
      if(...){
      if(views[i].dispatchTouchEvent(ev))
       return true;
       }
    }
    ...//其他处理,在此不管
  }
  /**
   * View
   * @param ev
   * @return
   */
  public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他处理,在此不管
    return false;
  }

在此可以看出,ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件,事实上子View的dispatchTouchEvent方法真正执行的代码是这样的

/**
   * View
   * @param ev
   * @return
   */
  public boolean dispatchTouchEvent(MotionEvent ev){
    ....//其他处理,在此不管
    return onTouchEvent(event);
  }

一般情况下,我们不该在普通View内重写dispatchTouchEvent方法,因为它并不执行分发逻辑。当Touch事件到达View时,我们该做的就是是否在onTouchEvent事件中处理它。

那么,ViewGroup的onTouchEvent事件是什么时候处理的呢?当ViewGroup所有的子View都返回false时,onTouchEvent事件便会执行。由于ViewGroup是继承于View的,它其实也是通过调用View的dispatchTouchEvent方法来执行onTouchEvent事件。

 

在目前的情况看来,似乎只要我们把所有的onTouchEvent都返回false,就能保证所有的子控件都响应本次Touch事件了。但必须要说明的是,这里的Touch事件,只限于Acition_Down事件,即触摸按下事件,而Aciton_UP和Action_MOVE却不会执行。事实上,一次完整的Touch事件,应该是由一个Down、一个Up和若干个Move组成的。Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理。当所有子View的onTouchEvent都返回false时,这次的Touch请求就由根ViewGroup,即Activity自己处理了。

看看改进后的ViewGroup的dispatchTouchEvent方法

View mTarget=null;//保存捕获Touch事件处理的View
  public boolean dispatchTouchEvent(MotionEvent ev) {

    //....其他处理,在此不管
    
    if(ev.getAction()==KeyEvent.ACTION_DOWN){
      //每次Down事件,都置为Null

      if(!onInterceptTouchEvent()){
      mTarget=null;
      View[] views=getChildView();
      for(int i=0;i<views.length;i++){
        if(views[i].dispatchTouchEvent(ev))
          mTarget=views[i];
          return true;
      }
     }
    }
    //当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move
    if(mTarget==null){
      return super.dispatchTouchEvent(ev);
    }
    //...其他处理,在此不管
    if(onInterceptTouchEvent()){
     //...其他处理,在此不管  
     }
//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
    return mTarget.dispatchTouchEvent(ev);

  }

ViewGroup还有个onInterceptTouchEvent,看名字便知道这是个拦截事件。这个拦截事件需要分两种情况来说明:

1.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该ViewGroup的所有下发操作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。由于mTarge为null,该ViewGroup的onTouchEvent事件被执行。这种情况下可以把这个ViewGroup直接当成View来对待。

2.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Acion为Down的Touch事件都返回false,其他的都返回True,这种情况下,Down事件能正常分发,若子View都返回false,那mTarget还是为空,无影响。若某个子View返回了true,mTarget被赋值了,在Action_Move和Aciton_UP分发到该ViewGroup时,便会给mTarget分发一个Action_Delete的MotionEvent,同时清空mTarget的值,使得接下去的Action_Move(如果上一个操作不是UP)将由ViewGroup的onTouchEvent处理。

情况一用到的比较多,情况二个人还未找到使用场景。

从头到尾总结一下:

1.Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

2.ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

4.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

5.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

6.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。
 补充:

“触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。”,这里补充下其实UP事件是可能为0个的。
以上就是本文的全部内容,希望对大家理解Touch事件分发机制有所帮助。

相关文章

  • Android百度地图添加Marker失真问题的解决方案

    Android百度地图添加Marker失真问题的解决方案

    本篇文章主要介绍了Android百度地图添加Marker失真问题的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Android 自定义view实现进度条加载效果实例代码

    Android 自定义view实现进度条加载效果实例代码

    这篇文章主要介绍了Android 自定义view实现进度条加载效果实例代码,需要的朋友可以参考下
    2017-08-08
  • Android线程间通信 Handler使用详解

    Android线程间通信 Handler使用详解

    这篇文章主要为大家介绍了Android线程间通信Handler使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 详解Android四种存储方式

    详解Android四种存储方式

    在Android程序开发中我们经常遇到四种数据存储方式,每种存储方式都各不相同,下面通过本篇文章给大家介绍android四种存储方式,对此感兴趣的朋友一起学习吧
    2015-12-12
  • Android实现弹出列表、单选、多选框

    Android实现弹出列表、单选、多选框

    这篇文章主要为大家详细介绍了Android实现弹出列表、单选、多选框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • Android下载gradle失败的解决方法

    Android下载gradle失败的解决方法

    这篇文章主要介绍了Android下载gradle失败的解决方法,文章通过图文结合的方式给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-06-06
  • 如何在XML中定义菜单

    如何在XML中定义菜单

    这篇文章主要为大家详细介绍了在XML中定义菜单的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Android中RecyclerView点击Item设置事件

    Android中RecyclerView点击Item设置事件

    这篇文章主要介绍了Android中RecyclerView点击Item设置事件的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • Android 开发中Volley详解及实例

    Android 开发中Volley详解及实例

    这篇文章主要介绍了Android 开发中Volley详解及实例的相关资料,需要的朋友可以参考下
    2017-04-04
  • android图像绘制(三)画布刷屏问题记录

    android图像绘制(三)画布刷屏问题记录

    在canvas中绘制动态图的时候,如果使用了一个固定的背景图片,只有一个小小的精灵在移动!这样的情况下却不得不在没帧中重新绘制背景图片,使得效率降低,本文章只是记录一下,并不是解决方法感兴趣的朋友可以了解下
    2013-01-01

最新评论