• 这边博文不是原创,应该算作读书笔记,综合了下面几篇博文,做了一些删减合并,得到适合我自己看的笔记。

  • Android:30分钟弄明白Touch事件分发机制

  • Android 编程下 Touch 事件的分发和消费机制

  • View事件分发及消费源码分析

  • 从 onTouchEvent() 的返回值来看 View 的事件传递

一、以Touch事件为例的分发机制

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

  • 下面这张图阐释了ViewGroup和View的关系

  • ViewGroup的相关事件有三个:onInterceptTouchEvent(事件拦截
    )、dispatchTouchEvent(事件分发)、onTouchEvent(事件响应)。

  • View的相关事件只有两个:dispatchTouchEvent(事件分发)、onTouchEvent(事件响应)。

1、dispatchTouchEvent

  • 首先,最先接收到触摸事件的是 Activity,然后是Activity的 ViewGroup,然后会依次下发,下发的过程调用子View(ViewGroup)的dispatchTouchEvent方法实现。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述图片例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。dispatchTouchEvent的返回值来自onTouchEvent。来个简单版的代码加深理解:
    /** * ViewGroup */    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 */    public boolean dispatchTouchEvent(MotionEvent ev) {        return onTouchEvent(event);    }
  • 一个表层的结论:onTouchEvent 返回 true 说明该 View 消耗了触摸事件,后续的触摸事件也由它来进行处理。返回 false 说明该 View 对触摸事件不感兴趣,事件继续传递下去。

  • 在此可以看出,ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent,onTouchEvent会根据View的处理方式返回boolean类型的变量。

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

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

  • 当所有子View的onTouchEvent都返回false时,这次的Touch请求就由根ViewGroup,即Activity自己处理了。这里的Touch事件,只限于Acition_Down事件,而Aciton_UP和Action_MOVE却不会执行。事实上,一次完整的Touch事件,应该是由一个Down、一个Up和若干个Move组成的。Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理;Down返回false的View不会触发Up和Move,只有Down返回true时才会触发Up和Move事件。

2、onInterceptTouchEvent

  • 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、ViewGroup.dispatchTouchEvent(event)

private boolean dispatchTouchEvent(MotionEvent event) {    int action = event.getAction();    //判断ViewGroup是否拦截touch事件。当为ACTION_DOWN或者找到能够接收touch事件的子View时,由onInterceptTouchEvent(event)决定是否拦截。其他情况,即ACTION_MOVE/ACTION_UP且没找到能够接收touch事件的子View时,直接拦截。    boolean intercepted;    if (action == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {        intercepted = onInterceptTouchEvent(event);    } else {        intercepted = true;    }    //如果ViewGroup不拦截touch事件。在ACTION_DOWN时遍历所有子View,查找能够接收touch事件的子View。如果找到则设置mFirstTouchTarget,并跳出循环。    boolean alreadyDispatchedToNewTouchTarget =  false;    if (!intercepted) {        if (action == MotionEvent.ACTION_DOWN) {           for (int i = childrenCount - 1; i >= 0; i--) {                if (!canViewReceivePointerEvents(child) ||                 !isTransformedTouchPointInView(x, y, child, null)) {                     continue;                }                if (dispatchTransformedTouchEvent(event, child)) {                   //找到mFirstTouchTarget                   newTouchTarget = addTouchTarget(child);                   alreadyDispatchedToNewTouchTarget = true;                   break;                }             }         }    }    //事件下发及消费。如果没找到能够接收touch事件的子View,则由ViewGroup自己处理及消费。如果找到能够接收touch事件的子View,则由子View递归处理touch事件及消费。    boolean handled = false;    if (mFirstTouchTarget == null) {        handled = dispatchTransformedTouchEvent(event, null);    } else {        if (alreadyDispatchedToNewTouchTarget) {            handled = true;        } else {            while (touchTarget) {                handled = dispatchTransformedTouchEvent(event, child);            }        }    }    return handled;}//ViewGroup事件下发。如果无接收touch事件的子View,则由ViewGroup的父类(即View)下发touch事件。如果child非空,则交由子View下发touch事件,子View可以是ViewGroup或View。boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {   boolean handled;   if (child == null) {        handled = super.dispatchTouchEvent(event);   } else {        handled = child.dispatchTouchEvent(event);   }   return handled;}

2、View.dispatchTouchEvent(event)

//View的Touch事件分发。当外部设置了mOnTouchListener时,先交由mOnTouchListener.onTouch(event)消费。若未消费,则交给View的onTouchEvent(event)消费。onTouchEvent的实现是,如果设置了mOnClickListener,则执行mOnClickListener.onClick()点击事件。返回值为true,表示消费,否则未消费。boolean dispatchTouchEvent(MotionEvent event) {   boolean result = false;   if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {         result = true;   }   if (!result && onTouchEvent(event)) {        result = true;   }   return result;}boolean onTouchEvent(MotionEvent event) {   performClick();}

三、总结

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事件。

更多相关文章

  1. 《Android第一行代码》书籍划重点记录
  2. Android(安卓)nine-patch .9使用方法入门
  3. Android之——杀死用户选中的进程优化
  4. Xamrin开发安卓笔记(一)
  5. 赵雅智_HttpClient的 get请求和post请求
  6. [置顶] AIDL使用以及原理分析
  7. Android(安卓)外观模式
  8. 【移动开发】Android中WIFI开发总结(二)
  9. 对在Android的状态栏添加Menu,Back,Home的改进

随机推荐

  1. Android(安卓)UI 测试框架 Espresso
  2. Android获取当前进程名
  3. Android UI之RelativeLayout(相对布局)
  4. 拥抱Android
  5. 原来Android还可以这样通过反射,获取jar包
  6. irrlicht移植到android
  7. android 自定义搜索 的简单实现
  8. Android(安卓)MediaScanner使用简介
  9. Android中WebView和JavaScript进行简单通
  10. [置顶] Android系统架构