当手指触摸屏幕的时候,首先事件来到Activity的dispatchTouchEvent方法,看下源码里的这个方法:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
如果是按下事件,调用onUserInteraction方法,这个方法的方法体为空。
接着是调用getWindow().superDispatchTouchEvent(ev),如果返回值为true,则dispatchTouchEvent方法返回也就为true,下面的onTouchEvent方法就不会执行了。这里的getWindow(),其实获取到的是一个PhoneWindow对象,往这个类里面看看:
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
调用了DecorView的superDispatchTouchEvent(),最后又调用了ViewGroup的dispatchTouchEvent方法,所以这整个过程就是Activity-->PhoneWindow-->DecorView-->ViewGroup。
事件分发主要看这三个方法:dispatchTouchEvent() 用于分发事件
onInterceptTouchEvent() 事件拦截
onTouchEvent() 事件响应
这三个方法返回值代表的涵义大致如图
先从ViewGroup的dispatchTouchEvent方法说起,源代码太长这里就取关键的内容来说:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
}
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
// this is weird, we got a pen down, but we thought it was
// already down!
// XXX: We should probably send an ACTION_UP to the current
// target.
mMotionTarget = null;
}
// If we're disallowing intercept or if we're allowing and we didn't
// intercept
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// reset this event's action (just to protect ourselves)
ev.setAction(MotionEvent.ACTION_DOWN);
// We know we want to dispatch the event down, find a child
// who can handle it, start with the front-most child.
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
// offset the event to the view's coordinate system
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
// Event handled, we have a target now.
mMotionTarget = child;
return true;
}
// The event didn't get handled, try the next view.
// Don't reset the event's location, it's not
// necessary here.
}
}
}
}
}
看这一句代码:if (disallowIntercept || !onInterceptTouchEvent(ev)) 如果disallowIntercept 或者 !onInterceptTouchEvent(ev)的值有一个为true就进入里面的代码,这里disallowIntercept表示不允许拦截,这个值默认为false,可以通过requestDisallowInterceptTouchEvent方法来设置。而onInterceptTouchEvent(ev)方法我们知道,默认返回也是false的,这个方法如果设置返回true就代表这个事件我拦截下来了,就不会往下面传递了。
if (child.dispatchTouchEvent(ev)) {
// Event handled, we have a target now.
mMotionTarget = child;
return true;
}
这里调用了子View的dispatchTouchEvent(ev),如果它返回true,那本ViewGroup的dispatch就返回true,下面的onTouchEvent方法就不会收到事件了,所以就说明了我们平时如果在一个布局里嵌套子View,点击子View的部分只响应子View的触摸事件和点击事件,而父View的就不会再触发。
那如果点击的不是子View部分呢,看这部分代码 :
if (target == null) {
// We don't have a target, this means we're handling the
// event as a regular view.
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
就会调用super.dispatchTouchEvent(ev),而super也就是View类,意思也就是按照本身的触摸事件来执行了。
View的dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
}
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
可以看到是调用了本身的onTouch()或者onTouchEvent方法。