对于View的事件分发,其实就是对MotionEvent对象的分发。
事件分发的顺序:
Actiivty——>Window——>ViewGroup——>View
涉及事件分发有三个特别重要的方法:
public boolean dispatchTouchEvent(MotionEvent ev)
:用来进行事件的分发,如果点击事件能够传递给当前VIew,那么此方法就一定会被调用public boolean onInterceptTouchEvent(MotionEvent event)
:用来判断是否拦截某个事件,只存在于ViewGroup,在dispatchTouchEvent(MotionEvent ev)
内部调用public boolean onTouchEvent(MotionEvent event)
:用来处理点击事件。在dispatchTouchEvent(MotionEvent ev)
方法内部调用
Activity的事件分发
当一个点击事件发生时,事件最先传到Activity
的dispatchTouchEvent(MotionEvent ev)
进行事件分发:
//在Activity中
public boolean dispatchTouchEvent(MotionEvent ev) {
//一般事件的开始都是Down事件
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//实现屏保的功能
onUserInteraction();
}
//交给window进行事件分发
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//当所有的VIew都不处理该事件时,Activity的onTouchEvent方法调用
return onTouchEvent(ev);
}
//该方法为空,当此Activity在栈顶的时候,触屏点击home,back,menu建等都会触发此方法
public void onUserInteraction() {
}
//在PhoneWindow中.
//在Activity的dispatchTouchEvent方法中,getWindow所得到的Winodw就是PhoneWindow,它是Window的子类
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
//mecor,是顶层View,即DecorView的对象
}
//在DecorView中
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
//DecorView继承了FrameLayout,是所有界面的父类;
//调用父类的方法,即调用了ViewGroup的dispatchTouchEvent方法
//即事件交了ViewGroup去进行处理
}
//在Activity中
//当Activity下的任何一个子View都没有接收/处理事件时,该方法被调用
public boolean onTouchEvent(MotionEvent event) {
//判断是否是Window边界外的触摸事件
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;// 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false
}
//在Wiundow中
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
final boolean isOutside =
event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
|| event.getAction() == MotionEvent.ACTION_OUTSIDE;
if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
return true;
}
return false;
// 返回true:说明事件在边界外,即 消费事件
// 返回false:未消费(默认)
}
private boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
final View decorView = getDecorView();
return (x < -slop) || (y < -slop)
|| (x > (decorView.getWidth()+slop))
|| (y > (decorView.getHeight()+slop));
}
Activity事件分发总结:
ViewGourp的事件分发
上面我们分析到,Activity会将事件分发交给ViewGroup:
//在DecorView中
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
//DecorView继承了FrameLayout,是所有界面的父类;
//调用父类的方法,即调用了ViewGroup的dispatchTouchEvent方法
//即事件交了ViewGroup去进行处理
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//代码省略
... ...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
//如果Action为ACTION_DOWN,那么就会对该事件做重置状态的操作
//在下面方法中,会对FLAG_DIAALLOW_INTERCEPT进行重置,
//因此对子View调用requstDsallowInterceptTouchEvent方法并不能影响ViewGourp对
//ACTION_DOWN事件的处理
resetTouchState();
}
// 检查是否拦截
final boolean intercepted;
//判断事件类型是不是为MotionEvent.ACTION_DOWN
//或mFirstTouchTarget != null
//mFirstTouchTarget 表示是否事件被子元素成功处理了,也就是ViewGroup是否拦截了
//当子元素成功处理事件时,mFirstTouchTarget会被赋值并指向子元素.
//也就是说当ViewGroup不拦截事件并将事件交给子元素时mFirstTouchTarget != null,
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//FLAG_DISALLOW_INTERCEPT可以通过requestDisallowInterceptTouchEvent方法来设置。
//一旦设置,ViewGroup将无法拦截除了ACTION_DOWN以外的其他点击事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//询问是否拦截,默认为不拦截
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
//以上可以看出,如果ViewGourp一旦拦截事件,那么后续的事件将会默认交给它处理
//并且不会再调用onInterceptTouchEvent(ev)方法。(除ACTION_DOWN)
... ...///代码省略
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//检查事件是否取消或被拦截
if (!canceled && !intercepted) {
... ... //代码省略
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
//得到点击事件在子元素中的坐标
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
//考虑到两个View交叉重合的情况,下面的先放进集合,
//但是按常理说我们手指先按到上面的,所以这里做了一个倒序
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//倒序遍历所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);//得到子控件的绘制顺序
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//判断子元素是否能够接收到点击事件:
//1. 子元素是否在动画
//2. 点击事件的坐标是否落在子元素的区域内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//在getTouchTargerget方法中会遍历循环得到一个view;
//在一般情况下,事件还没有分发给子View时,newTouchTarget为null
//mFirstTouchtarget是一个单链表的
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
//重点
//dispatchTransformedTouchEvent方法中,会判断传入的child是否为null,
//如果不为null,就会将直接调度child.dispatchTouchEvent方法,
//这样事件就交给了子元素去处理。
//如果为null,就会调用super.dispatchTouchEvent方法,
//View(ViewGroup继承自View)会自行处理点击事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
//进此方法,说明子元素分发了事件
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//mFistTouchTarget在addTouchTarget中赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//记录已经有子元素处理该点击事件
alreadyDispatchedToNewTouchTarget = true;
//跳出循环
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}//循环块结束
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}//判断是否取消和拦截的if块结束
... ... //代码省略
}
**重点看看dispatchTransformedTouchEvent
方法:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
//进行事件分发
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);
}
// Done.
transformedEvent.recycle();
return handled;
}
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
如果在遍历完子View后,ViewGroup仍然没有找到事件处理者,即ViewGroup并没有子View处理了事件或者是子View处理了事件,但是子View的dispatchTouchEvent
方法返回了false(一般是由于子View的onTouchEvent方法返回了false),那么ViewGroup会去处理这个事件。
在上面代码中,如果dispatchTransformedTouchEvent
这个方法返回false,那么mFistTouchTarger肯定为null,那么事件就交给ViewGroup,其实就是交给了View:
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
前面我们说过,如果一开始mFistTouchTarget不为null且Action不为ACTION_DOWN时,就说明已经有子元素处理该事件了,那么就不会再去进行拦截询问,那么它会直接进行事件分发:
else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
ViewGroup的事件分发总结:
View的事件分发:
从ViewGourp的事件分发,我们知道,View的事件分发也是从dispatchTouchEvent
方法开始的。
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
//查看是否有窗口覆盖在上面
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//当View的onTouchLListener不为空
//且View是enable状态
//且onTouch方法返回true时,
//result = ture
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果result为true,那么onTouchEvent就不会调用,
//这说明View的onTouchListener的优先级高于onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
如果我们给View设置了OnTouchListener监听,并且在回调方法onTouch()中返回true,View的onTouchEvent就得不到执行,其dispatchTouchEvent方法就会直接返回true给父容器,相反如果返回false,或者没有设置OnTouchListener监听,才会执行onTouchEvent()方法对分发来的事件进行处理。
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//只要View是可点击的(设置了上面三个属性其中一个),即使View的enable属性为disable,
//View的onTouchEvent都会返回ture,即消耗此事件。
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return clickable;
}
//如果View设置了代理,就会执行mTouchDelegate.onTouchEvent(event)
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//下面是onTouchEvent中对点击事件的具体处理
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;
}
//查看是否处理prepressed状态
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//试图用Hanlder去post这个mPerformClick,
//PerformClick是Runnable的实现类
//在它的run方法中会调用performClick()
if (!post(mPerformClick)) {
//在performClick方法中,如果View设置了OnClickListener,
//那么perforCliick方法内部会调用它的onClick方法
performClick();
}
}
}
... ... //代码省略
break;
... ... //代码省略
}
//只要可以点击,就会返回ture
return true;
}//if块结束
return false;
}
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
只要View处于可点击状态,并且进入了switch的判断逻辑,就会被返回true,表明该事件被消费掉了,也就是说只要View是可点击的,事件传到了其OnTouchEvent,都会被消费掉。而平时我们在调用setOnClickListener方法给View设置点击事件监听时,都会将其点击状态修改为可点击状态。
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
当View的onTouchEvent返回ture后,Dwon事件被消耗掉,会到ViewGroup的dispatchTouchEvent方法
,dispatchTransformedTouchEvent
方法ture,这个时候会调用addTouchTarget
方法,那么mFisrtTouchTarget就不为null了,当Move,Up事件来到的时候,
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE)
不成立,就直接来到下面的代码,调用dispatchTransformedTouchEvent
方法继续进行分发,待子View进行消费。
View的事件分发总结:
流程梳理总结:
当我什么都不做处理,点击Button的时候:
对于ACTION的类型:
- 0:ACTION_DOWN
- 1:ACTION_UP
- 2:ACTION_MOVE
//先是DWON事件
D/MainActivity: dispatchTouchEvent: 111 0
D/FrameRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
D/FrameRoot: onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111
D/ButtonView: onTouchEvent: 111
D/ButtonView: onTouchEvent: true
dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//然后是MOVE事件
D/MainActivity: dispatchTouchEvent: 111 2
D/FrameRoot: dispatchTouchEvent: 111 2
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 2
onInterceptTouchEvent: 111
D/RelativeRoot: onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//最后是UP事件
D/MainActivity: dispatchTouchEvent: 111 1
D/FrameRoot: dispatchTouchEvent: 111 1
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 1
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
如果我在RelativeLayout中最拦截,但是没有重写onTouchEvent方法:
//DOWN事件
D/MainActivity: dispatchTouchEvent: 111 0
D/FrameRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: true
onTouchEvent: 111
onTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: false
D/FrameRoot: onTouchEvent: 111
onTouchEvent: false
dispatchTouchEvent: false
D/MainActivity: onTouchEvent: false
dispatchTouchEvent: false
//MOVE事件
D/MainActivity: dispatchTouchEvent: 111 2
onTouchEvent: false
dispatchTouchEvent: false
//UP事件
D/MainActivity: dispatchTouchEvent: 111 1
D/MainActivity: onTouchEvent: false
dispatchTouchEvent: false
尽管我在RelativeLayout的onInterceptTouchEvent
方法中返回ture,这个时候会调用它的onTouchEvent方法,但是我并没有重写onTouchEvent且RelativeLayout是不可点击的,所以返回false,那么RelativeLayout的dispatchTochEvent
方法返回false,而且mFisrtTouchTarget == null,所以当MOVE或UP事件来到时,只会到DecorView,intercepted = true;
,直接调用父类的dispatchTouchEvent
方法,从DecorView就回到Activity了。
如果我在RelativeLayout中拦截,且重写onTouchEvent方法,返回ture:
//DOWN事件
D/MainActivity: dispatchTouchEvent: 111 0
D/FrameRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: true
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//MOVE事件
D/MainActivity: dispatchTouchEvent: 111 2
D/FrameRoot: dispatchTouchEvent: 111 2
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 2
onTouchEvent: 111
onTouchEvent: true
D/RelativeRoot: dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//UP事件
D/MainActivity: dispatchTouchEvent: 111 1
D/FrameRoot: dispatchTouchEvent: 111 1
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 1
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
我们可以看到后续的事件都只传到RelativeLayout
如果我不在RelativeLayout做拦截,在onTouchEvent中返回ture,在Button在onTouchEvent中返回false:
//DOWN事件
D/MainActivity: dispatchTouchEvent: 111 0
D/FrameRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 0
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/ButtonView: dispatchTouchEvent: 111
D/ButtonView: onTouchEvent: 111
onTouchEvent: false
dispatchTouchEvent: false
D/RelativeRoot: onTouchEvent: 111
D/RelativeRoot: onTouchEvent: true
dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//MOVE事件
D/MainActivity: dispatchTouchEvent: 111 2
D/FrameRoot: dispatchTouchEvent: 111 2
onInterceptTouchEvent: 111
D/FrameRoot: onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 2
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
//UP事件
D/MainActivity: dispatchTouchEvent: 111 1
D/FrameRoot: dispatchTouchEvent: 111 1
onInterceptTouchEvent: 111
onInterceptTouchEvent: false
D/RelativeRoot: dispatchTouchEvent: 111 1
onTouchEvent: 111
onTouchEvent: true
dispatchTouchEvent: true
D/FrameRoot: dispatchTouchEvent: true
D/MainActivity: dispatchTouchEvent: true
我们可以看到,只有Down事件去向Button分发了,后面的UP事件和MOVE事件都没有,因为在RelativeLayout调用dispatchTouchEvent
方法时,mFisrtTouchTarget 为null,直接调用父类的dispatchTouchEvent
方法,然后调用了onTouchEvent
方法。