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事件。来个简单版的代码加深理解:

  1. /**
  2. * ViewGroup
  3. * @param ev
  4. * @return
  5. */
  6. public boolean dispatchTouchEvent(MotionEvent ev){
  7. ....//其他处理,在此不管
  8. View[] views=getChildView();
  9. for(int i=0;i<views.length;i++){
    //判断下Touch到屏幕上的点在该子View上面 
    if(...){
  10. if(views[i].dispatchTouchEvent(ev))
  11. return true;
    }
  12. }
  13. ...//其他处理,在此不管
  14. }
  15. /**
  16. * View
  17. * @param ev
  18. * @return
  19. */
  20. public boolean dispatchTouchEvent(MotionEvent ev){
  21. ....//其他处理,在此不管
  22. return false;
  23. }

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

  1. /**
  2. * View
  3. * @param ev
  4. * @return
  5. */
  6. public boolean dispatchTouchEvent(MotionEvent ev){
  7. ....//其他处理,在此不管
  8. return onTouchEvent(event);
  9. }

一般情况下,我们不该在普通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方法

  1. View mTarget=null;//保存捕获Touch事件处理的View
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3.  
  4. //....其他处理,在此不管
  5.  
  6. if(ev.getAction()==KeyEvent.ACTION_DOWN){
  7. //每次Down事件,都置为Null
  8.  
  9. if(!onInterceptTouchEvent()){
    mTarget=null;
  10. View[] views=getChildView();
  11. for(int i=0;i<views.length;i++){
  12. if(views[i].dispatchTouchEvent(ev))
  13. mTarget=views[i];
  14. return true;
  15. }
    }
  16. }
  17. //当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move
  18. if(mTarget==null){
  19. return super.dispatchTouchEvent(ev);
  20. }
  21. //...其他处理,在此不管
  22. if(onInterceptTouchEvent()){
  1. //...其他处理,在此不管
    }
  1. //这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
  2. return mTarget.dispatchTouchEvent(ev);
  3. }

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个的。

最近刚好在做一个手势放大缩小移动图片的Demo,对此有了更多的理解。对于onInterceptTouchEvent事件,它的应用场景在很多带scroll效果的ViewGroup中都有体现。设想一下再一个ViewPager中,每个Item都是个ImageView,我们需要对这些ImageView做Matrix操作,这不可避免要捕获掉Touch事件,但是我们又需要做到不影响ViewPager翻页效果,这又必须保证ViewPager能捕获到Move事件,于是,ViewPager的onInterceptTouchEvent会对Move事件做一个过滤,当适当条件的Move事件(持续若干事件或移动若干距离,这里我没读源码只是猜测)触发时,并会拦截掉,返回子View一个Action_Cancel事件。这个时候子View就没有Up事件了,很多需要在Up中处理的事物要转到Cancel中处理。

Android:30分钟弄明白Touch事件分发机制的更多相关文章

  1. Android事件分发机制(二)30分钟弄明白Touch事件分发机制

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

  2. Android面试必问!View 事件分发机制,看这一篇就够了!

    在 Android 开发当中,View 的事件分发机制是一块很重要的知识.不仅在开发当中经常需要用到,面试的时候也经常被问到. 如果你在面试的时候,能把这块讲清楚,对于校招生或者实习生来说,算是一块不 ...

  3. Android的Touch事件分发机制简单探析

    前言 Android中关于触摸事件的分发传递是一个很值得研究的东西.曾不见你引入了一个ListView的滑动功能,ListView就不听你手指的指唤来滚动了:也不知道为啥Button设置了onClic ...

  4. Android Touch事件分发机制学习

    Android  事件分发机制 ViewGroup dispatchTouchEvent 返回true dispatchTouchEvent: Activity ACTION_DOWN Myrelat ...

  5. Android开发之Touch事件分发机制

    原地址http://www.cnblogs.com/linjzong/p/4191891.html Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实 ...

  6. 【转】Android:Touch事件分发机制

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

  7. Activity Window View WindowManager关系&Touch事件分发机制

    http://www.cnblogs.com/linjzong/p/4191891.html https://www.cnblogs.com/kest/p/5141817.html https://b ...

  8. Touch事件分发机制

    原文:http://www.cnblogs.com/linjzong/p/4191891.html Touch事件分发中只有两个主角:ViewGroup和View.Activity的Touch事件事实 ...

  9. Android Touch事件分发机制

    参考:http://blog.csdn.net/xiaanming/article/details/21696315 参考:http://blog.csdn.net/wangjinyu501/arti ...

随机推荐

  1. bsearch

    在java中为了避免 low+high溢出,可以用无符号右移:正数高位补0,负数高位补1 int mid = (low + high) >>> 1; 如果是在c++中,那么需要先转换 ...

  2. HDU 4043 FXTZ II (组合数学-排列组合)

    FXTZ II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  3. linux移植问题汇总(一)

    linux移植问题汇总(一) 在此记录移植linux过程中出现的问题以及解决方法. 项目GitHub地址 linux3.0.80:https://github.com/numbqq/linux-3.0 ...

  4. Android开发学习总结(一)——搭建最新版本的Android开发环境

    Android开发学习总结(一)——搭建最新版本的Android开发环境(转) 最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Android开发虽然有所了解,但是 ...

  5. APMServ 配置记录

    下载版本是5.2.6,它采用压缩率更高的7zip 解压后拷贝到c盘 点击APMServ.exe就可以启动了 注意: 1. SSL 有时需要关掉,否则apache起不来 2. 多数时候我只用apache ...

  6. poj 2342 Anniversary party 简单树形dp

    Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3862   Accepted: 2171 ...

  7. STL的erase()陷阱-迭代器失效总结

    下面材料整理自Internet&著作. STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector .deque):另一类是以不连续的节点形式存储的容器(如:list.s ...

  8. NOIP2013普及组 -SilverN

    T1  计数问题 题目描述 试计算在区间 1 到 n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次?例如,在 1 到 11 中,即在 1.2.3.4.5.6.7.8.9.10.11 中, ...

  9. Remote Displayer for Android

    应用截图: 作者:sunrain_hjb      QQ:2157825357                 Email:sunrain_hjb@aliyun.com 版本:1.0.188     ...

  10. AutoIT 实现Firefox上传文件

    Firefox浏览器文件上传代码如下: ;upload file Func _UploadFile($file) AutoItSetOption("WinTitleMatchMode&quo ...