核心方法:
ONE:dispatchTouchEvent(MotionEvent ev):处理整个事件的核心所在,事件分发由它执行,重要到什么程度
如果你自定义一个Layout并且重写这个方法将其代码清空那么这个ViewGroup以及它的所有子View将都无法接收任何事件
TWO:onInterceptTouchEvent(MotionEvent ev) :用于事件的拦截,就是决定这个事件是否继续传递(见本文),
既然传递就联系到ViewGroup与View,而android本身的规则,事件由顶向外层传递,也就是ViewGroup向View
传递, 而View并没有子View,也就是说View的控件是没有这个方法的。当然我们可以决定是否往下继续传递。
THERE:onTouchEvent(MotionEvent event) :这个都很熟悉了,代码写在里面可以响应触摸事件(见本文)。
第一步:对屏幕产生一个触摸事件, 这个事件被包装成MotionEvent对象。
第二步:这个对象被发送到Activity,并且调用Activity的dispatchTouchEvent:看源码:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction(); //内部为空方法。
}
if (getWindow().superDispatchTouchEvent(ev)) { //核心所在
return true;
}
return onTouchEvent(ev);
}
上述代码的核心就是调用了getWindow().superDispatchTouchEvent(ev) 而Windows是一个抽象类,具体的实现为PhoneWindow,
所以看PhoneWindows的此方法:
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor应该都很熟悉吧,做最顶层的View它本身就是个FrameLayout,我们DecorView点进去最后来到了ViewGroup里并且调用了
dispatchTouchEvent(MotionEvent ev)方法。
所以上述过程总结: 触摸屏幕 产生一个MotionEvent对象,该对象包含触摸的相关信息,而这个对象经过Activity转到DecorView再到根ViewGroup。
所以一切的开端都是ViewGroup的dispatchTouchEvent(MotionEvent ev)。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) { //判断是否为DOWN事件。
resetTouchState(); //初始化操作核心代码
}
//只添加了我们步骤的核心代码,
}
第三步:判断这个事件是否为DOWN事件并且做一些初始化操作,因为触摸屏幕总是以Down开始UP结束。看源码:
private void resetTouchState() {
clearTouchTargets(); // 核心代码
} 接着看:
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle(); //回收这个对象
target = next;
} while (target != null);
mFirstTouchTarget = null; //核心,将其置空
}
}
上述的mFirstTouchTarget对象才是我们所要看到的,初始化就是让mFristTouchTarget=null,
请注意:一定是Down事件时才会初始化,否则会保留上一次的mFristTouchTarget.
mFirstTouchTarget又是什么呢? 继续看源码:
private static final class TouchTarget {
private static TouchTarget sRecycleBin;
private static int sRecycledCount;
public View child;
public TouchTarget next;
//截取现需的
}
ViewGroup的内部类,这个类包含一个VIew以及本身, 这个View是什么? 当我们触摸的那个View如果消耗了这个
事件那么这个View就会标记那个View,next一般标记为null,我们注意这个View就行。ok mFirstTouchTarget=null后
第三步也就完成了。
第四步: 回到dispatchTouchEvent里继续往下看代码:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
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 {
intercepted = true;
}
}
上述代码就是接着初始化后进行的,其实就是判断是否拦截。intercepted=true表示拦截,
拦截不拦截其实就是dispatchTouchEvent里的部分代码执行或不执行。
先看看:
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) 如果是一个down事件或者非down事件但是mFristTouchTarget
已经不是初始化时的值时会执行。 那么mFristTouchTarget什么时候会为非初始化值呢, 为了方便理解,笔者提前说下
后面会用源码证实。 mFristTouchTarget只有在这个ViewGroup的child消耗了这个事件的时候才会不为null。
要注意的是一切都从DeCorView开始
满足条件后又做了什么呢? 接着看:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
disallowIntercept作用是过滤, 如果是down事件值为false,非down事件值为true。也就是说down事件才会继续执行:
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} 在这里调用了onInterceptTouchEvent(ev); 根据返回值判断是否拦截事件。也就是说只有down事件才会调用决定是否拦截的代码。
如果为非down事件则直接为intercepted = false 不拦截;
而 if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
为false时会执行
else {
intercepted = true;
}
也就是说非down事件,并且child没有消耗这个事件时会拦截。
将上述总结一下:一个触摸事件对其判定有两种:
ONE: Down事件,直接调用onInterceptTouchEvent(ev)决定是否拦截。
TWO:非Down事件,如果这个非Down事件的起始事件(Down事件)没有拦截,
并且后续操作中消耗了这个事件,那么这个非Down事件就不会拦截。
如果这个非Down事件的起始事件(Down事件)没有拦截,
但是后续操作中并没有消耗这个事件,那么这个非Down事件就会拦截,
如果这个非Down事件的起始事件(Down事件)拦截了,
那么这个非Down事件就会被拦截。
第五步: 拦截与不拦截会有那些不同呢?
public boolean dispatchTouchEvent(MotionEvent ev) {
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
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;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
} //上述代码均在循环结构里
这一步骤大致看到的是遍历子View,那么前提肯定是不拦截了:
if (!canceled && !intercepted) { //如果intercepted=false则执行。
接着分析:
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; //跳出本次的Child对象
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue; //跳出本次的Child对象
}
上述我们看到了一个循环,然后进行筛选,不满足的就跳出当前。所以上述代码执行完后能剩下的肯定就是可以接受本次事件的:
继续分析:
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
第五步刚开始就会调用TouchTarget newTouchTarget = null;
也就是说newTouchTarget在循环前就会被置空,所以它只存活于一个事件的过程,我们知道mFristTouchTarget也属于TouchTarget类,
而上文我们说过mFristTouchTarget中有View用于存放消耗事件的View,那么接着看getTouchTarget(child)执行了那些操作:
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
if (target.child == child) {
return target;
}
}
return null;
上述过程就是判断如果target!=null,也就是mFristTouchTarget!=null,也就是说存在消费事件的View,同理也能说明当前非Down事件时
如果循环得到的View与Down的View相同那就将这个mFristTouchTarget返回,因此newTouchTarget==mFristTouchTarget。
所以mFtistTouchTarget!=null if (newTouchTarget != null) 成立 于是退出了循环,读者可能会疑惑为什么要这么做?这里留下一个问题X 继续看循环里源码:
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
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();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
这一块是核心所在了,
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
先说明一下,整个循环结构能到这一步就表示:ONE:当前事件为Down事件,
TWO:已经找到了合适的Child。
为什么说当前事件时Down事件, 因为能执行这个循环结构就表示事件未拦截,
事件未拦截就有两种情况: 1: Down事件, 2:非Down事件且存在消耗事件的View, 为什么不会是情况2呢,
看看上面的这个:
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
我们知道newTouchTarget==mFristTouchTager 如果为非Down事件那么这些就会执行然后退出循环下面自然执行不到。
所以上面的问题X自然就清楚了, 就是防止这些代码的执行,那为什么要防止这些代码执行, Down执行了一次,而非Down就不让执行呢?
看里面代码:
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
newTouchTarget = addTouchTarget(child, idBitsToAssign); 看addTouchTarget内部:
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target; //注意整个事件的过程只有在这里给mFristTouchTarget添加了对象。
return target;
}
说明一下:TouchTarget.obtain 会创建一个TouchTarget将内部的View指向参数的Child,然后mFristTouchTarget=target,最后
newTouchTarget=target,所以newTouchTarget==mFristTouchTarget, 这些过程都发生在Down事件里,一定要记住mFristTouchTarget
只有在Down事件过程才有可能添加对象。
所以上述问题为什么非Down不允许运行。因为没有必要多次执行耗时并且相同的动作。
细心的你可能又留意到了,一个问题的解决又产生了一个问题, 为什么说可能会添加对象。请注意:上述过程前提是
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) 成立的情况下。那就看一看它的源码是什么:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
transformedEvent = MotionEvent.obtain(event); //根据当前的event产生一个效果相同的MotionEvent
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);
}
return handled;
}
解释: child为循环找到的view,ev为触摸事件, transforedEvent的效果与event相同(可能是为了防止某些东西被修改吧),
然后进行了判断,
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
}else {
handled = child.dispatchTouchEvent(transformedEvent);
}
有两个问题: 1: 为什么child可以为null,
2: super.dispatchTouchEvent与child.dispatchTouchEvent有何不同
问题1: 我们当前在循环里执行的方法传入的child确实不为null,但是在循环外部child却可以为null,所以child的不同引起了问题2。
问题2:这个问题有着至关重要的作用, 牢记:我们知道ViewGroup是继承View的所以super. dispatchTouchEvent就是View.dispatchTouchEvent
而ViewGroup又重写了View的dispatchTouchEvent方法,所以导致super.dispatchTouchEvent与ViewGroup.dispatchTouchEvent有差异,
child.dispatchTouchEvent又是什么呢,我们知道child是这个ViewGroup的子类,是子类就会有两种情况:一个是View;一个是ViewGroup
所以child.dispatchTouchEvent介于两者之间。child是ViewGroup的话就会执行我们上述研究的dispatchTouchEvent方法。
下面进行第六步:我们来简短的分析一下super.dispatchTouchEvent源码:
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
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;
}
}
return result;
}
我们重点关注这些代码, onFilterTouchEventForSecurity(event)是一种安全策略,一般为true。
ListenerInfo li = mListenerInfo; ListenerInfo是什么类呢? 点进去:
static class ListenerInfo {
protected OnFocusChangeListener mOnFocusChangeListener;
public OnClickListener mOnClickListener;
protected OnLongClickListener mOnLongClickListener;
private OnTouchListener mOnTouchListener;
...
}
可以看到ListenerInfo 是一个专门用于存放View的各种监听事件的类,
一般情况一个View都会有一个mListenerInfo。 看看一个重写的监听事件对象怎么存放进去:
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
getListenerinfo的源码:
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
getListenerinfo拿到了当前View的mListenerInfo,然后将我们重写的对象放入mListenerInfo管理。回到上文:
li=mListenerInfo后li便可以执行监听事件操作了。接着分析:
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
上述可以看到我们的给View添加的onTouch()返回true时。 result就会返回true。
记着这个结论:onTouch()返回true,result=true;这时候也就说明我们的onTouch()方法已经执行了。
if (!result && onTouchEvent(event)) {
result = true;
}
这个是接着上述代码的, 如果result=true那么就不会执行,onTouchEvent()方法也就不会执行。
所以onTouch()的优先级要高于onTouchEvent(),
稍微总结一下: OnTouch()的优先级要高于OnTouchEvent();
OnTouch()返回值为true,onTouchEvent()不会执行。
ok 然后我们研究一下这个result, 这也是一个至关重要的存在,注意super.dispatchTouchEvent的返回值:
return result;它是返回result的,千万别忽视super.dispatchTouchEvent或者child.dispatchTouchEvent返回值。
我们仔细想一想前面我们代码中写过这个返回值返回给谁?
handled = super.dispatchTouchEvent(transformedEvent);
handled = child.dispatchTouchEvent(transformedEvent);
handled是哪个方法里面的?没错来自于
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
return handled;
}
这个方法最后返回的是handled, 也就是返回result, 那么返回给谁呢? 这时又回到了我们开始的地方
是不是在循环里面有过这样的代码:
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
上面我们是不是说过这个步骤可能会给mFristTouchTarget添加对象,添加对象就是执行了这个:
newTouchTarget = addTouchTarget(child, idBitsToAssign); 不够清楚的话再回到上面看看。
而决定是否添加mFristTouchTarget就是上述判断,也就是handled,result的值
也就是它child的OnTouch,OnTouchEvent的返回值。
请注意:这个child值的是离得当前View最近的child无论是View还是ViewGroup,
打个比方, A里面有B,B里面有C, 那如果这个方法是在A里面的话child指的就是B。
总结一下: 经过上述过程我们知道了,child的OnTouch()或者OnTouchEvent()返回值会直接影响调用它的dispatchTouchEvent()方法的Parent,
具体怎么影响呢? 会使得Parent的mFristTouchTarget是否为null。 返回值为true 则mFristTouchTarget!=null。
这里产生一个很重要的问题: mFristTouchTarget是否为null对整个事件分发有什么影响呢? 继续看循环外面的代码:
第七步:
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
......}
可以看到在循环外部又判断了mFristTouchTarget为空,
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
执行了在循环内部一模一样的操作。 来回顾一下要执行循环内部dispatchTransformedTouchEvent()需要那些条件?
整个循环结构能到这一步就表示:ONE:当前事件为Down事件,
TWO:已经找到了合适的Child。
这两个方法执行条件有什么不同呢?循环里的:child已经找到。
事件为Down。
这里的: mFristTouchTarget=null;
onTouch()和onTouchEvent()都返回false。
请注意: 循环外的此方法由于child==null,所以最终会执行super.dispatchTouchEvent()
也就是执行自己的onTouch()或者onTouchEvent();
执行时期: Down事件{
A:拦截
B:未拦截,child未消耗。
}
非Down事件{
A:拦截
}
循环里的此方法child!=null,所以会执行child.dispatchTouchEvent()
所以最终会执行super.dispatchTouchEvent()或者ViewGroup.dispatchTouchTarget()
完全取决于child是View还是ViewGroup, 若是ViewGroup则会执行我们从头开始分析的过程
进而形成一种循环,直到当前拿到的事件被拦截,或者当前的child为View。
执行时期: Down事件{
A:未拦截
}
非Down事件{
无法执行。
}
提醒:这里的child指的是离当前最近的.
打个比方 A里有B,B里有C, A执行dispatchTouchEvent()会把事件传给B。同理
第八步: 如果循环里的dispatchTransformedTouchEvent()执行时期是Down事件并且未拦截
那么假如一个View消耗了Down事件,所以后续的非Down事件都要传给它,但是它已经无法接收了,那么后续的事件如何响应呢?
接着看else{...}:
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;
}
}
注意能调用else{..}已经表示child消耗了事件。
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
无论是Down还是非Down ,只要事件被消耗那mFristTouchTarget!=null, 而newTouchTarget始终==mFristTouchTarget
所以target==newTouchTarget成立, 然后注意alreadyDispatchedToNewTouchTarget,在Down事件里有过这样代码:
alreadyDispatchedToNewTouchTarget = true; 所以一旦执行Down后else{..}就直接return:
而在后续的非Down事件中,由于alreadyDispatchedToNewTouchTarget默认为false,则会执行
else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
内部是不是又将事件交给child执行了,自己返回true。 这样一个完整的事件分发机制就ok了。
最后来个大总结:
区分拦截与不拦截:
拦截会导致自己的mFristTouchTarget==null,进而调用自己的OnTouch或者onTouchEvent
不拦截则会将事件交给child根据child的返回值判断事件是否消耗。
而拦截与不拦截最终还是要决定是否消耗事件的。无非就是拦截调用自己的方法,不拦截调用child的方法。
关于事件的消耗:
onTouch()或者onTouchEvent()的返回值都会决定事件是否消耗。
对于一个Down事件,它的所有View都能收到(默认不拦截情况下)(仅仅只是收到),
但是是否执行onTouch或者onTouchEvent()的掌控权就先交给了最末端的View(非ViewGroup,它的Parent全是ViewGroup)
如果这个View表示不消耗(肯定会执行一次onTouch或者onTouchEvent),那就会再次执行这个View最近的Parent的onTouch或者onTouchEvent,然后决定权就交给了它,
然后就一直向上传递。 如果有一个ViewGroup表示可以消耗,那么Down事件的后续事件都会交给它,其他的View都不会参与(这个ViewGroup后的不能接收)。
其实机制就是当一个View或者ViewGroup表示消耗时往后的View或者ViewGroup里的mFristTouchTarget=null(包括自己),往前(向DecorView) 的mFristTouchTarget!=null。
进而导致调用了super(自己)或者child的处理事件方法。
如果最末端的View表示可以消耗,那么所有的ViewGroup的mFristTouchTarget!=null, 后续的事件就全交给这个View执行。
如果ViewGroup和View都表示消耗,则最末端的优先级最高!
Down拦截: 如果一个ViewGroup对Down事件拦截,那么事件将不能继续往下传递,而后续的MOVE,UP事件能否处理取决于这个ViewGroup的Down事件返回值,为true 则都交给这个ViewGroup,MOVE与UP事件的返回值不影响
MOVE拦截: 如果一个ViewGroup的Child在Down事件中返回true,则默认后续事件都交给它,但此时如果在这个ViewGroup的MOVE中拦截,那么后续的MOVE,UP事件都会交给ViewGroup
UP拦截: 这个拦截没有意义,当Down事件默认被Child处理这时ViewGroup表示UP拦截,那么那个都不能接收UP事件,如果在这个UP事件之前得事件都被这个ViewGroup拦截那么无论UP是否拦截,则都交给ViewGroup所以说UP拦截没有意义
对于上述拦截的前提是有事件能传递过来,而事件能传递过来的前提就是child的Down事件返回为true,这样才能保证它的Parent有机会拦截后续事件。