源码
/**
* {@inheritDoc}
*/
@Override
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;
//mGroupFlags可以通过requestDisallowInterceptTouchEvent赋值
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//首先处理ACTION_DOWN事件
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null; //mMotionTarget置为空
}
如果disallowIntercept为true表示不允许viewgroup拦截事件,或者onInterceptTouchEvent返回false,那么就会把该ACTION_DOWN事件交给子View
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
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];
//该子view必须是可见的或者没有动画
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)) { //如果子View的dispatchTouchEvent方法处理ACTION_DOWN事件返回true,那么就赋值mMotionTarget
// Event handled, we have a target now.
mMotionTarget = child; //当子View处理了ACTION_DOWN事件,就说明找到了事件的target了。。
return true;//子View处理了ACTION_DOWN事件,直接返回,不执行后续代码。
}
}
}
}
}
}
//如果action是ACTION_UP,ACTION_CANCEL。isUpOrCancel为true
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
// Note, we've already copied the previous state to our local
// variable, so this takes effect on the next event
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// The event wasn't an ACTION_DOWN, dispatch it to our target if
// we have one.
//如果是ACTION_MOVE,ACTION_UP,ACTION_CANCEL事件则会执行到这里
final View target = mMotionTarget;
//如果mMotionTarget为null,那么就交给该ViewGroup处理事件。。说明如果子View中dispatchTouchEvent中处理ACTION_DOWN事件返回false,那么后续的事件都交给viewgroup。
//或者ACTION_DOWN事件中onInterceptTouchEvent返回了true,那么包括ACTION_DOWN事件的所有事件都交给viewgroup,子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);
}
// if have a target, see if we're allowed to and want to intercept its
// events
if (!disallowIntercept && onInterceptTouchEvent(ev)) { //如果拦截了
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL); //生成一个ACTION_CANCEL事件交给子View处理
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// clear the target
mMotionTarget = null; //如果拦截了,那么把mMotionTarget置空
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;
}
//如果当前事件是up或者cancel,也会把mMotionTarget置空
if (isUpOrCancel) {
mMotionTarget = null;
}
// finally offset the event to the target's coordinate system and
// dispatch the event.
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
//如果执行到了这一步,就说明该事件交给子View处理了
return target.dispatchTouchEvent(ev);
}
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
由上面可知:
首先我们知道: Down Move Up事件依次发生
子View的dispatchTouchEvent处理Down事件返回值才会影响后续事件的继续处理。。如果返回了false,那么该子view都接收不到后续的Move Up事件。。
如果dispatchTouchEvent处理Move事件,返回false/true,都不会影响up事件的处理的。。子view都会处理
/×这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。
简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。×/
前面部分是对的,后面就是错的了。 dispatchTouchEvent处理Move事件时并不会影响Up事件。
但是如果在Move事件时把事件给拦截了,那么就会影响Up事件了,因为此时把mMotionTarget置为null了,那么move事件和up事件其实都是交给viewgroup处理
为了验证这个问题,代码如下:
主界面:
<lbb.demo.test.MyLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<lbb.demo.test.MyButton
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="MyButton"/>
</lbb.demo.test.MyLinearLayout>
测试代码
例子1
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyLinearLayout dispatchTouchEvent:" + event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyLinearLayout onTouchEvent:" + event.getAction());
return super.onTouchEvent(event);
}
}
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyButton dispatchTouchEvent:" + event.getAction()); //0:Down 1:Up 2:Move 3:Cancel
if (event.getAction() == MotionEvent.ACTION_MOVE) {
super.dispatchTouchEvent(event);//先调用这个,否则onTouchEvent调用不到
return false;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyButton onTouchEvent:" + event.getAction()); //0:Down 1:Up 2:Move 3:Cancel
return super.onTouchEvent(event);
}
}
打印结果:
11-05 03:33:05.219 13771-13771/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:0
11-05 03:33:05.219 13771-13771/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:0
11-05 03:33:05.219 13771-13771/lbb.demo.test D/LiaBin: MyButton onTouchEvent:0
11-05 03:33:05.283 13771-13771/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:2
11-05 03:33:05.283 13771-13771/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:2
11-05 03:33:05.283 13771-13771/lbb.demo.test D/LiaBin: MyButton onTouchEvent:2
11-05 03:33:05.306 13771-13771/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:2
11-05 03:33:05.306 13771-13771/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:2
11-05 03:33:05.306 13771-13771/lbb.demo.test D/LiaBin: MyButton onTouchEvent:2
11-05 03:33:05.683 13771-13771/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:1
11-05 03:33:05.683 13771-13771/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:1
11-05 03:33:05.683 13771-13771/lbb.demo.test D/LiaBin: MyButton onTouchEvent:1
可以看到,虽然dispatchTouchEvent中ACTION_MOVE事件返回了false,但是依然不影响UP事件的接收
例子2
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyLinearLayout dispatchTouchEvent:" + event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return true;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyLinearLayout onTouchEvent:" + event.getAction());
return super.onTouchEvent(event);
}
}
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyButton dispatchTouchEvent:" + event.getAction()); //0:Down 1:Up 2:Move 3:Cancel
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(BaseApp.TAG, "MyButton onTouchEvent:" + event.getAction()); //0:Down 1:Up 2:Move 3:Cancel
return super.onTouchEvent(event);
}
}
打印结果:
11-05 03:30:04.192 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:0
11-05 03:30:04.192 10976-10976/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:0
11-05 03:30:04.192 10976-10976/lbb.demo.test D/LiaBin: MyButton onTouchEvent:0
11-05 03:30:04.233 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:2
11-05 03:30:04.233 10976-10976/lbb.demo.test D/LiaBin: MyButton dispatchTouchEvent:3
11-05 03:30:04.233 10976-10976/lbb.demo.test D/LiaBin: MyButton onTouchEvent:3
11-05 03:30:04.249 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:2
11-05 03:30:04.249 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout onTouchEvent:2
11-05 03:30:04.266 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:2
11-05 03:30:04.266 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout onTouchEvent:2
11-05 03:30:04.524 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout dispatchTouchEvent:1
11-05 03:30:04.524 10976-10976/lbb.demo.test D/LiaBin: MyLinearLayout onTouchEvent:1
可以看到,如果viewgourp中拦截了move事件,那么move事件和后续的up事件都交给viewgroup了。。同时注意这里生成了一个cancel事件,见源代码分析
高版本源码
以上的都是针对2.3.7以下的,如果以上,代码就不一样了。。但是本质上是一样的
可以参考 Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)
如果更深层一步了解 Android触摸屏事件派发机制详解与源码分析三(Activity篇)