1.ViewGroup中的mFirstTouchTarget是一个什么东西,它有什么作用?
在ViewGroup中有一个类型为TouchTrarget的mFirstTouchTarget的成员变量,它是用来保存消费事件的子View的信息的。代码如下:
private static final class TouchTarget {
@UnsupportedAppUsage
public View child;
public TouchTarget next;
// ...省略无关代码
}
可以看到TouchTarget内部保存了一个View和一个类型为TouchTarget的next成员变量,也就是说TouchTarget是一个链表结构。为什么是链表结构呢?主要是因为Android系统是支持多点触控的,所以TouchTarget设计成了链表。
设计mFirstTouchTarget的目的是为了避免在所有的事件序列中都去递归查找要消费事件的View,只需要在ACTION_DOWN中递归查找消费的View,并将View封装后赋值为mFirstTouchTarget,避免了后续一系列事件的查找。
mFirstTouchTarget会在ACTION_DOWN的时候被赋值,查找是否有能够消费事件的子View,如果有则将这个View包装成TouchTarget赋值给mFirstTouchTarget,否则mFirstTouchTarget为null。
接下来的一系列ACTION_MOVE事件都会根据mFirstTouchTarget是否为null和onInterceptTouchEvent来判断是否要拦截事件。所以mFirstTouchTarget在事件分发的流程中占了非常重要的作用。
2.如果在ViewGroup中拦截了ACTION_DOWN事件会怎样?
首先来看ViewGroup的dispatchTouchEvent方法的伪代码:
// ViewGroup
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 调用自身的onInterceptTouchEvent方法来判断是否拦截事件
intercepted = onInterceptTouchEvent(ev);
} else {
intercepted = true;
}
// 如果在ACTION_DOWN中拦截了事件,那么intercepted恒为true,则无法给mFirstTouchTarget赋值
if (!canceled && !intercepted) {
mFirstTouchTarget = findConsumeChild(this);
}
if (mFirstTouchTarget == null) {
// 则调用自身的dispatchTouchEvent方法分发事件
handled = super.dispatchTouchEvent(event);
} else{
// 则调用子View的dispatchTouchEvent方法
handled = mFirstTouchTarget.child.dispatchTouchEvent(event);
}
return handled;
}
从上面的代码中可以看到,如果在onInterceptTouchEvent方法中拦截ACTION_DOWN事件,则intercepted为true,而intercepted为true直接导致了mFirstTouchTarget无法被赋值。
接下来,一系列ACTION_MOVE以及ACTION_UP等事件都无法再调用onInterceptTouchEvent方法,也就是intercepted恒为true,且mFirstTouchTarget恒为null。
再往下,由于mFirstTouchTarget恒为null,就导致了所有的Motion事件(也包括ACTION_DOWN事件)只能交由自身处理,无法再将事件分发给子View。
这一点在事件分发流程中非常重要,通过Down事件确定了要处理该事件的View,接下来所有的Move事件序列就不会再走递归,而是直接交给这个View来
3.为什么设置了onTouchListener后onClickListener不会被调用?
// View
public boolean dispatchTouchEvent(MotionEvent event) {
//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;
}
在View的dispatchTouchEvent中如果li.mOnTouchListener不为null,则调用li.mOnTouchListener.onTouch,而如果li.mOnTouchListener.onTouch返回了true,则下边的onTouchEvent就不会被调用,而onClickListener就是在onTouchEvent方法中才被调用的。
// 伪代码
public boolean onTouchEvent(MotionEvent event) {
public boolean onTouchEvent(MotionEvent event) {
case MotionEvent.ACTION_UP:
li.mOnClickListener.onClick(this);
break;
}
}
4.为什么一个View设置了setOnTouchListener会有提示没有引用performClick方法的警告?
当你添加了一些点击操作,例如像setOnClickListener这样的,它会调用performClick才可以完成onClick方法的调用,但你重写了onTouch,就有可能使得performClick没有被调用,这样这些点击操作就没办法完成了,所以就会有了这个警告。