为了便于讲解,本文只采用了两层来讲,即:ViewGroup包含一个View 。这里有必要讲一下,事件分发是从最顶层的持有dispatchTouchEvent()方法的View或ViewGroup开始的。逐层向下分发。
ViewGroup部分
ViewGroup持有dispatchTouchEvent()方法,我们先看看源码,看看它是如何向下分发的。
@Override
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); // 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;
}
}
通过简化后的源码,可以看到,调用了dispatchTouchEvent()方法后,方法体内有onInterceptTouchEvent方法。那么,如果用户自定义ViewGroup后,重写了dispatchTouchEvent方法,但是,返回值并不是调用的super.dispatchTouchEvent(), 而是直接返回true或者false,那么,onInterceptTouchEvent都将不会执行。所以,这里一定要搞清楚。并不是像网上有些文章写的,返回true,就是分发,返回false,就不分发了,事实上,分发动作是在ViewGroup的dispatchTouchEvent内完成的。所以,返回true和false都一样,都不会分发。而且,都不会执行onTouch和onClick 了。 具体为什么,还是看dispatchTouchEvent()剩下部分的源码:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!canceled && !intercepted) {
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
}
}
}
}
// 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);
}
}
简化后,可以看到,不管拦截不拦截,都执行了一个方法,那就是dispatchTransformedTouchEvent ,这个方法时干啥的? 待会讲。这里先讲下,不拦截,方法体力的for循环在干啥,通过阅读源码,我这里没贴出来,可以看到,是在找可以向下分发的view,找到后,调用了dispatchTransformedTouchEvent去分发。那拦截了怎么也调用了呢?因为上面讲过,ViewGroup是不具有onTouch和onTouchEvent方法的。那怎么办?子类没有,父类有呀,ViewGroup的父类是View,那就还是要分发到View上去处理。dispatchTransformedTouchEvent就是这个工具,将其分发过去的工具。所以,这里拦截后,只处理ViewGroup自己事件。不管子view的事件了。
到这里,ViewGroup就讲完了。
View部分
View部分就简单多了,没有onInterceptTouchEvent , 而onTouch 和onTouchEvent又都在dispatchTouchEvent方法内,所以只许看这个方法就行了。
public boolean dispatchTouchEvent(MotionEvent event) {
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
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;
}
看看源码,一下子就这么明了了。就是这么简单,和ViewGroup一样,你要是重写View的dispatchTouchEvent方法后返回值给改成true或false,就和onTouch及onTouchEvent 说拜拜了。这两兄弟被你屏蔽了。 再看看onTouch和onTouchEvent啥关系。源码中清晰的写了。如果onTouch方法返回值为true, 那么result=true,那么下面的一个if语句第一个条件就无法满足了,后面的onTouchEvent就不会执行,被扼杀了。而onTouchEvent不能执行,那么其方法体内的performClick方法就不会执行。perform不执行,onClickListener就不会有回调。onClick方法就不会执行了。
网友评论