一、概述
1.1 事件分发概述
**答:**当用户触摸屏幕(View 或 ViewGroup派生的控件),将产生点击事件(Touch事件)。Touch事件的相关细节(发生触摸的位置、时间等)被封装成MotionEvent对象,系统需把这个事件传递给一个具体的 View 去处理(消费)以及处理(消费)的整个过程。**即事件传递的过程及处理的整个过程,**其可能经过的对象有最上层Activity,中间层ViewGroup,最下层View。
1.2 事件的类型
| 事件类型 | 具体动作 |
|---|
| MotionEvent.ACTION_DOWN | 按下View(所有事件的开始) | | MotionEvent.ACTION_UP | 抬起View(与DOWN对应) | | MotionEvent.ACTION_MOVE | 滑动View | | MotionEvent.ACTION_CANCEL | 结束事件(非人为原因) |
1.3 什么是事件序列?

从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件是同一个事件序列。 一般情况下,事件序列以DOWN事件开始、UP事件结束,中间有无数的MOVE事件.
1.4 事件分发过程中共同协作的方法。
答:dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()。
dispatch:派遣、发出、传递。 intercept:拦截
| 方法 | 作用 | 调用时刻 |
|---|
| dispatchTouchEvent() | 分发(传递)点击事件 | 点击事件(Touch)传递给当前View时,dispatchTouchEvent()就会被调用。 | | onTouchEvent() | 处理点击事件 | 在dispatchTouchEvent()内部调用 | | onInterceptTouchEvent() | 判断是否拦截了某个事件(只存在于ViewGroup之中) | 在ViewGroup的dispatchTouchEvent()内部调用 |
1.5 事件在哪些对象之间传递?传递的顺序是什么?
答:当产生点击事件后会先由Activity来处理,在Activity , ViewGroup , View之间传递。
1.6 事件的传递过程
1.由上而下的传递过程
当一个View或ViewGroup的onInterceptTouchEvent()返回true,表示它要拦截这个MotionEvent,会调用它的dispatchTouchEvent(),返回false则将MotionEvent传递给子元素的dispatchTouchEvent。最后传给最底层View,无法再向下传递,就由底层View的onTouchEvent()处理。

2.由下而上的传递过程
如果onTouchEvent()返回true表示该View处理了,处理后逐层向dispatchTouchEvent()返回直至结束,返回false则代表没处理,继续向上层View的onTouchEvent()传递,最后由Activity的onTouchEvent()处理,不论处理结果是什么都结束事件分发。
1.7 ViewGroup怎么通过dispatchTouchEvent()把事件传递给自己的onTouchEvent?
答:dispatchTouchEvent()return true或false都不行,只能在其内部通过Interceptor()将事件拦截并调用自己的onTouchEvent(),因此ViewGroup的dispatchTouchEvent()super默认实现就是去调用此ViewGroup的onInterceptTouchEvent()进行拦截。
1.8 View有自己的dispatchTouchEvent()方法吗?
答:有,但View没有onInterceptTouchEvent()来判断是将事件传给子View还是拦截,因此View调用super.dispatchTouchEvent() 时默认把事件传给自己的onTouchEvent()处理(拦截)。dispatchTouchEvent()中返回false就回溯父元素的onTouchEvent()由父元素处理。
1.9 dispatchTouchEvent()中希望让自己的onTouchEvent()处理怎么操作?
答:默认处理super.dispatchTouchEvent()会调用自己的onInterceptTouchEvent(),在其中return true表示拦截就会把MotionEvent分发给自己的onTouchEvent()处理。
1.10 dispatchTouchEvent()中希望传给子View怎么操作?
答:默认处理super.dispatchTouchEvent()会调用自己的onInterceptTouchEvent(),在其中return false(不拦截且不消费),就会将MotionEvent交给子类。
1.11 希望不做处理、终止向下传递、开始回溯,怎么操作?
答:在每一层View的onTouchEvent()中return false;
1.12 onTouch和onTouchEvent的区别是什么?
答:两个方法都是在View.dispatchTouchEvent()中调用,源码如下:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
可以看到onTouch()优先于onTouchEvent()执行,如果onTouch方法中返回true将事件消费,onTouchEvent就不会执行。
onTouch()的执行需要mOnTouchListener不为空且当前点击的控件为ENABLED,对于非ENABLED控件(不可点击控件),我们如果想要监听其touch事件应该重写onTouchEvent(),因为它的onTouch()永远不会得到执行!
1.13 事件分发传递规则
- 一般情况下,一个事件序列只能被一个View拦截消耗,一旦拦截事件序列中所有事件会交给此View处理,不能由两个View同时处理,除非在
onTouchEvent()将MotionEvent传递给其他View。 - 一个
View的onTouchEvent()返回了false,那么同一事件序列都不会再交给此View处理,而是重新交给父元素的onTouchEvent()。 - 一旦某个View决定拦截,此View的
onInterceptTouchEvent()不会再被调用,不会再次询问去询问它是否拦截。 - 当
dispatchTouchEvent() 和 onTouchEvent() 都return true,事件传递就到达了终点(被消费),不会再继续传递。 - 当
dispatchTouchEvent()事件分发时,只有前一个事件(如ACTION_DOWN)返回true,才会收到后一个事件(ACTION_MOVE和ACTION_UP) - 若对象
(Activity、ViewGroup、View)的dispatchTouchEvent()分发事件后消费了事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP! - 若对象
(Activity、ViewGroup、View)的onTouchEvent()处理了事件(返回true),那么ACTION_MOVE、ACTION_UP的事件从上往下传到该View后就不再往下传递,而是直接传给自己的onTouchEvent(),结束本次事件传递过程!
二、事件分发源码分析
2.1 流程1:Activity的事件分发机制
Android的事件分发机制首先将MotionEvent传递给Activity的dispatchTouchEvent()进行事件分发。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Window是一个抽象类,其DispatchTouchEvent()也是抽象方法,该类可以控制顶级View的外观和行为策略。该类唯一实现是PhoneWindow,其中有一个内部类DecorView是一个ViewGroup。PhoneWindow中处理点击事件:
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
顶级View,即在Activity中通过setContentView()设置的View,也叫根View,此处调用的是ViewGroup的dispatchTouchEvent()方法,从而实现了事件由Activity→ViewGroup的dispatchTouchEvent()的过程。
流程图和方法图非常详尽:


ViewGroup的dispatchTouchEvent()什么时候返回true / false?接下来ViewGroup的事件分发流程会进行说明。
2.2 流程2:ViewGroup的事件分发机制
由Activity的事件分发机制可知,Activity.dispatchTouchEvent()中getWindow().superDispatchTouchEvent(ev)中super.dispatchTouchEvent(MotionEvent event)实现了Activity->ViewGroup的传递,ViewGroup的事件分发机制也由dispatchTouchEvent()进行。
public boolean dispatchTouchEvent(MotionEvent ev) {
...
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = **onInterceptTouchEvent(ev)**;
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex();
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
ViewGroup会用**onInterceptTouchEvent()**进行判断是否对该事件进行拦截,默认不拦截,如果拦截就用的onInterceptTouchEvent()会向View的dispatchTouchEvent()分发。
ViewGroup的onInterceptTouchEvent()源码
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
ViewGroup的dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)源码
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
final boolean handled;
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
遍历ViewGroup的子View或子ViewGroup,判断此次点击事件触摸区域是否属于它的子View或子ViewGroup的区域,如果属于子View区域就调用子View的dispatchTouchEvent(),即传递给子View处理,如果属于子ViewGroup,继续遍历子ViewGroup子视图,直到找到处理事件的View。如果遍历到最后还找不到就回调上一层的onTouchEvent()直到Activity。
流程图:

2.3 流程3:View的事件分发机制
View的事件分发同样由dispatchTouchEvent()开始
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
...
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
View的onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
...
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
return clickable;
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
mHasPerformedLongPress = false;
if (!clickable) {
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
break;
}
if (performButtonActionOnTouchDown(event)) {
break;
}
boolean isInScrollingContainer = isInScrollingContainer();
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
setPressed(true, x, y);
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
break;
case MotionEvent.ACTION_CANCEL://针对CANCEL事件,恢复各种状态,移除各种callback
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
case MotionEvent.ACTION_MOVE:
if (clickable) {
drawableHotspotChanged(x, y);
}
final int motionClassification = event.getClassification();
final boolean ambiguousGesture =
motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
int touchSlop = mTouchSlop;
if (ambiguousGesture && hasPendingLongPressCallback()) {
if (!pointInView(x, y, touchSlop)) {
removeLongPressCallback();
long delay = (long) (ViewConfiguration.getLongPressTimeout()
* mAmbiguousGestureMultiplier);
delay -= event.getEventTime() - event.getDownTime();
checkForLongClick(
delay,
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
touchSlop *= mAmbiguousGestureMultiplier;
}
if (!pointInView(x, y, touchSlop)) {
removeTapCallback();
removeLongPressCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
final boolean deepPress =
motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
if (deepPress && hasPendingLongPressCallback()) {
removeLongPressCallback();
checkForLongClick(
0 ,
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
}
break;
}
return true;
}
return false;
}

返回值分析:

- 如果
getWindow().superDispatchTouchEvent()返回true,也就是ViewGroup.dispatchTouchEvent()返回true,则方法结束,Activity的dispatchTouchEvent()也返回true,代表点击事件顺利从Activity传递到ViewGroup,ViewGroup分发后事件在其中被消费,Activity的分发任务结束,如果Activity的onTouchEvent()返回false,则Activity的dispatchTouchEvent()返回false,则代表事件没有被消费。 - 如果getWindow().superDispatchTouchEvent()方法返回false,则执行
Activity的onTouchEvent()方法,无论该方法返回true还是false,都表示此次事件分发都结束,ture表示点击事件在Window边界外(事件被Activity消费),false在Window边界内(事件不被处理,不算Activity消费)。
三、事件分发三个方法的流程图
3.1 dispatchTouchEvent()流程图



3.2 onInterceptTouchEvent()流程图
需要注意:只有ViewGroup有这个方法,Activity和View都没有

3.3 onTouchEvent()流程图


四、事件传递的几种情况
4.1 默认情况
不对控件里的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())进行重写。
需要注意的是:onTouchEvent()如果对DOWN事件返回了false,那么将不会再接收处理事件列的其他事件。但ViewGroup的onInterceptTouchEvent()对DOWN事件返回了false后后续事件仍会传递到它的onInterceptTouchEvent()

4.2 处理事件
如果View V希望处理点击事件,那么设置View V的(Clickable)或重写onTouchEvent()返回true
事件传递情况
- DOWN事件被传递给
V的onTouchEvent()并返回true,表示V要处理该事件 - V正在处理该事件,所以不会将
DOWN传递给ViewGroup和Activity的onTouchEvent() - 事件列中其他事件也会传递给
V 的onTouchEvent() - 如下图,也就是逐层向
dispatchTouchEvent()返回直至结束。

4.3 拦截DOWN事件
如果希望ViewGroup vg处理该点击事件,重写其onInterceptTouchEvent()返回true,onTouchEvent()返回true。
onInterceptTouchEvent()返回true表示事件不再向下传递- 自身
onTouchEvent()处理事件,不会传递给Activity的onTouchEvent() - 事件列内其他事件都传递给vg的
onTouchEvnet(),且不会再传递给vg的onInterceptTouchEvent(),因为一旦onInterceptTouchEvent()返回true就再也不会被调用。 - 逐层向
dispatchTouchEvent()返回,结束事件分发。

4.4 拦截事件列中间事件
如果ViewGroup vg拦截了一个MOVE,那么该事件会变为CANCEL并传递给在处理该事件的子View,且不传递给vg的onTouchEvent(),vg的onTouchEvent()会接收新到来的MOVE事件。
例如vg的子View v处理DOWN事件(v的onTouchEvent()返回true),但vg拦截了MOVE事件(vg的onInterceptTouchEvent()返回true拦截了MOVE),这个MOVE会变成CANCEL传递给v的onTouchEvent(),后续新来的MOVE会传递给vg的onTouchEvent(),而不是vg的onInterceptTouchEvent()。也就是后续事件直接由vg的onTouchEvent()处理。
  
|