在android的开发过程中,事件的分发是一个比较重要的知识体系,了解了事件分发机制有助于更好处理事件冲突导致滑动失效的问题。所谓的事件分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生之后,系统需要把这个事件传递给一个具体的view,而这个过程就是事件的分发过程。事件的分发过程由三个很重要的方法共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent,下面分别介绍这三个方法。

public boolean dispatchTouchEvent(MotionEvent event)
该方法是用来进行事件分发。如果事件能够传递给当前view,那么该方法一定会被调用,返回结果受当前view的onTouchEvent和下一级view的dispatchTouchEvent方法的影响,返回结果表示是否消耗当前事件。

public boolean onInterceptTouchEvent(MotonEvent event)
在上述方法内部调用,用来判断是否拦截当前事件,如果当前view拦截了当前事件,将会把事件传给当前view的onTouchEvent方法,返回结果表示是否拦截了当前事件。

public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEven方法内部调用,用来处理触摸事件,返回结果表示是否消耗当前事件,如果不消耗,则将会把事件向上进行传递。

分析ACTION_DOWN事件

先上张图,然后具体情况具体分析,如下:

默认状态

点击按钮,打印日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent
ChildView: onTouchEvent
FatherLayout: onTouchEvent
MainActivity: onTouchEvent

如果没有对控件里的方法进行重写或者更改返回值,那么整个事件分发的流程是从MainActivity(Activity)->FatherLayout(ViewGroup)->ChildView(View)从上往下调用dispatchTouchEvent方法(如果视图是ViewGroup,还会调用onInterceptTouchEvent方法),直到最后一个子视图(ChildView)的dispatchTouchEvent方法,在由ChildView(View)->FatherLayout(ViewGroup)->MainActivity(Activity)从下往上调用onTouchEvent方法,这个流程就是上图中,带有super箭头所走的路径,如果整个流程不被中断,整个事件的流程是一个U形图。

FatherLayout dispatchTouchEvent 返回true

点击按钮,打印的日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent

当FatherLayout dispatchTouchEvent方法返回true时,从上面的日志可以看出,事件没有继续向下进行传递,而是终止在了FatherLayout dispatchTouchEvent方法处。综上可知,当视图的dispatchTouchEvent方法返回true时,事件不会继续向下进行传递,而是在此处被该视图消费掉了。

FatherLayout dispatchTouchEvent 返回false

点击按钮,打印日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
MainActivity: onTouchEvent

当FatherLayout dispatchTouchEvent方法返回false时,从上面日志上可以看出,事件是回传给了MainActivity的onTouchEvent。

FatherLayout onInterceptTouchEvent 返回true

点击按钮,打印日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
FatherLayout: onTouchEvent
MainActivity: onTouchEvent

当FatherLayout onInterceptTouchEvent方法返回true时,表示该视图拦截了事件的传递,并将事件交给自己的onTouchEvent方法进行处理。

FatherLayout onInterceptTouchEvent 返回false

日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent
ChildView: onTouchEvent
FatherLayout: onTouchEvent
MainActivity: onTouchEvent

当FatherLayout onInterceptTouchEvent方法返回false时,打印的日志跟默认状态下是一样的,也就是说onInterceptTouchEvent返回false时,不会对事件传递产生任何的影响,跟返回super时是一样的。

ChildView dispatchTouchEvent 返回true

日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent

当ChildView dispatchTouchEvent方法返回true时,表现的形式跟FatherLayout是一样的。

ChildView dispatchTouchEvent 返回false

日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent
FatherLayout: onTouchEvent
MainActivity: onTouchEvent

当ChildView dispatchTouchEvent方法返回false时,表现的形式跟FatherLayout是一样的。
这里说明ViewGroup和View dispatchTouchEvent方法返回true和false时,对事件的拦截是一样的。

ChildView onTouchEvent 返回true

日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent
ChildView: onTouchEvent

从上面的日志可以看出,事件没有继续向下进行传递,而是终止在了ChildView onTouchEvent方法处。综上可知,当视图的dispatchTouchEvent方法返回true时,事件不会继续向下进行传递,而是在此处被该视图消费掉了。

ChildView onTouchEvent 返回false

日志如下:
MainActivity: dispatchTouchEvent
FatherLayout: dispatchTouchEvent
FatherLayout: onInterceptTouchEvent
ChildView: dispatchTouchEvent
ChildView: onTouchEvent
FatherLayout: onTouchEvent
MainActivity: onTouchEvent

当ChildView onTouchEvent方法返回false时,打印的日志跟默认状态下是一样的,也就是说onTouchEvent返回false时,不会对事件传递产生任何的影响,跟返回super时是一样的,继续将事件向上传递。

FatherLayout onTouchEvent不在做单独的分析。

总结

  1. ViewGroup和View当dispatchTouchEvent和onTouchEvent返回true时,代表对事件进行了拦截,自己消费掉该事件,当返回false的时候,都是回传到父视图的onTouchEvent方法。
  2. ViewGroup的onInterceptTouchEvent返回true时,代表对事件进行了拦截,并把事件传递给自己的onTouchEvent方法。
  3. ViewGroup的onInterceptTouchEvent返回false时,不会对事件进行拦截,跟返回super逻辑是一致的。
  4. View没有onInterceptTouchEvent方法,如果View需要将事件传递给自己的onTouchEvent方法,只需dispatchTouchEvent方法返回super即可。

分析ACTION_MOVE和ACTION_UP事件

默认情况

日志如下
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

从日志上看出,只有MainActivity的dispatchTouchEvent方法和onTouchEvent方法响应了ACTION_MOVE和ACTION_UP事件,接下来看下其他情况。

MainActivity dispatchTouchEvent返回true

日志如下
MainActivity: dispatchTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP

FatherLayout dispatchTouchEvent返回true

日志如下
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
FatherLayout: dispatchTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
FatherLayout: dispatchTouchEvent ACTION_UP

ChildView dispatchTouchEvent返回true

日志如下
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
FatherLayout: dispatchTouchEvent ACTION_MOVE
FatherLayout: onInterceptTouchEvent ACTION_MOVE
ChildView: dispatchTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
FatherLayout: dispatchTouchEvent ACTION_UP
FatherLayout: onInterceptTouchEvent ACTION_UP
ChildView: dispatchTouchEvent ACTION_UP

从上面三种情况可以看出,当Activity、ViewGroup、View的dispatchTouchEvent 方法返回true时,ACTION_MOVE和ACTION_UP事件传递的层级跟ACTION_DOWN是一样的。

ChildView onTouchEvent返回true

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
FatherLayout: dispatchTouchEvent ACTION_MOVE
FatherLayout: onInterceptTouchEvent ACTION_MOVE
ChildView: dispatchTouchEvent ACTION_MOVE
ChildView: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
FatherLayout: dispatchTouchEvent ACTION_UP
FatherLayout: onInterceptTouchEvent ACTION_UP
ChildView: dispatchTouchEvent ACTION_UP
ChildView: onTouchEvent ACTION_UP

FatherLayout onTouchEvent返回true

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
FatherLayout: dispatchTouchEvent ACTION_MOVE
FatherLayout: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
FatherLayout: dispatchTouchEvent ACTION_UP
FatherLayout: onTouchEvent ACTION_UP

MainActivity onTouchEvent返回true

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

从上面三种情况可以看出,当Activity、ViewGroup、View的onTouchEvent方法返回true时,ACTION_MOVE和ACTION_UP的事件从上往下传到当前view时就不再往下传递了,而是直接传给自己的onTouchEvent方法消费并结束本次事件传递过程。

MainActivity dispatchTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP

通过上面对ACTION_DOWN的事件分析,当dispatchTouchEvent返回false时,会将事件传递给父视图的onTouchEvent方法,但是MainActivity属于最上层的视图,在dispatchTouchEven在向上传递事件时,是找不到可以接受该事件的视图,所以该事件由自己的dispatchTouchEvent方法给消费掉,ACTION_MOVE和ACTION_UP只在MainActivity dispatchTouchEvent传递。

FatherLayout dispatchTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

ChildView dispatchTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

MainActivity onTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

FatherLayout onTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

ChildView onTouchEvent返回false

MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

FatherLayout onInterceptTouchEvent返回true

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

FatherLayout onInterceptTouchEvent返回false

日志如下:
MainActivity: dispatchTouchEvent ACTION_DOWN
FatherLayout: dispatchTouchEvent ACTION_DOWN
FatherLayout: onInterceptTouchEvent ACTION_DOWN
ChildView: dispatchTouchEvent ACTION_DOWN
ChildView: onTouchEvent ACTION_DOWN
FatherLayout: onTouchEvent ACTION_DOWN
MainActivity: onTouchEvent ACTION_DOWN
MainActivity: dispatchTouchEvent ACTION_MOVE
MainActivity: onTouchEvent ACTION_MOVE
MainActivity: dispatchTouchEvent ACTION_UP
MainActivity: onTouchEvent ACTION_UP

当FatherLayout和ChildView dispatchTouchEvent方法返回false,MainActivity、FatherLayout、ChildView onTouchEvent方法返回false,FatherLayout onInterceptTouchEvent方法返回super、true、false时,由于这些事件中途没被其他视图消费掉,而是正常到达MainActivity的onTouchEvent方法上,所以ACTION_MOVE和ACTION_UP只在 MainActivity dispatchTouchEvent和onTouchEvent上传递。

总结

  1. 当Activity、ViewGroup、View的dispatchTouchEvent 方法返回true时,ACTION_MOVE和ACTION_UP事件传递的层级跟ACTION_DOWN是一样的。
  2. 当Activity、ViewGroup、View的onTouchEvent方法返回true时,ACTION_MOVE和ACTION_UP的事件从上往下传到当前view时就不再往下传递了,而是直接传给自己的onTouchEvent方法消费并结束本次事件传递过程。
  3. 当事件最终被最上层视图的onTouchEvent方法消费时,ACTION_MOVE和ACTION_UP只在最上层视图的dispatchTouchEvent和onTouchEvent上传递。

更多相关文章

  1. Android事件总线之EventBus3.0基本使用
  2. Android(安卓)获取内外置存储卡方法
  3. Android(安卓)Junit 单元测试、异步测试方法简介及异步测试框架
  4. Android源码阅读分析:从Activity开始(二)——加载布局
  5. Android中Fragment的应用(android官方教程完美翻译)
  6. 【Android】Uri、UriMatcher、ContentUris详解 .
  7. [置顶] 史上最全selector和shape使用方法 Android(安卓)ListView
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. mysql8.0.19基础数据类型详解
  2. Mysql事务隔离级别原理实例解析
  3. Mysql5.7及以上版本 ONLY_FULL_GROUP_BY
  4. windows10下 MySQL msi安装教程图文详解
  5. mysql update语句的执行过程详解
  6. MySQL MyISAM默认存储引擎实现原理
  7. SQL注入漏洞过程实例及解决方案
  8. MySql如何使用not in实现优化
  9. MySQL如何使用union all获得并集排序
  10. MySQL select、insert、update批量操作语