Android事件分发机制

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

先上张图,然后具体情况具体分析,如下:
Android事件分发机制_第1张图片

默认状态

点击按钮,打印日志如下:
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

Android事件分发机制_第2张图片

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

MainActivity dispatchTouchEvent返回true

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

Android事件分发机制_第3张图片

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

Android事件分发机制_第4张图片

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

Android事件分发机制_第5张图片

从上面三种情况可以看出,当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

Android事件分发机制_第6张图片

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

Android事件分发机制_第7张图片

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

Android事件分发机制_第8张图片

从上面三种情况可以看出,当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

Android事件分发机制_第9张图片

通过上面对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

Android事件分发机制_第10张图片

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

Android事件分发机制_第11张图片

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

Android事件分发机制_第12张图片

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

Android事件分发机制_第13张图片

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

Android事件分发机制_第14张图片

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

Android事件分发机制_第15张图片

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

Android事件分发机制_第16张图片

当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上传递。

你可能感兴趣的:(android)