开个头
在上一篇我们已经把一个事件从Activity–>PhoneWindow–>DecorView–>ViewGroup这个过程,通过源码详细分析了一波。
这一篇,我们主要是通过源码,来分析一个事件通过ViewGroup的dispatchTouchEvent(ev)方法,传到View的dispatchTouchEvent(ev)方法之后的过程。(我们就先假设ViewGroup里面都是View,没有ViewGroup。因为如果一个ViewGroup里面还有ViewGroup那么他之后的过程,和他父ViewGroup传给他的过程是一样的)
接着上一篇,事件的传递规则
现在那个ACTION_DOWN事件通过下面的代码传到了View。也就是传递给了View.dispatchTouchEvent(ev)方法。
//通过这个方法把ViewGroup吧事件传了 child的dispatchTouchEvent(ev)
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispa tchTouchEvent(event);
} else {
//因为是通过ViewGroup遍历过来的,所以child肯定不为null,就执行了这个方法,把事件传到了child里面
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
}
好,接下里,话不多说,直接View.dispatchTouchEvent(ev)的源码,我们还是只看关键代码。
android.view.View
//看着很简单嘛,代码很少。
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
//这个我们就先默认他是true,直接进去看里面的代码。
if (onFilterTouchEventForSecurity(event)) {
ListenerInfo li = mListenerInfo;
//这个if语句应该看的很清楚了。
//主要判断了,1.当前的View是否设置了OnTouchListener,2.并且onTouch()方法是否返回true
//如果以上两个条件都成立了,那么result=true;
//如果以上两个条件有一个不成立,那么result=false;
//很简单接着往下看。
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果上面的两个条件都成立,那么result=true,这个if不会走,也就说,不会执行View的onTouchEvent(ev)
//这个结论在上一篇我们有提到,在这里我们就给出了解释。
//result=true了, 那么就到最后的return result;了,也就是ViewGroup的dispatchTouchEvent()
//方法也返回true了。到此ACTION_DOWN就结束了,这个事件由ouTouch方法处理,由当前的View消耗了。
//如果上面条件不成立,就是要么没有设置OnTouchListener要么,设置了但是onTouch()返回false了。
//那么result=false,这个时候才会走onTouchEvent(event)方法。
//然后就看onTouchEvent(event)的返回值了。
//如果返回true,那么result也为true。表示ACTION_DOWN事件被View的onTouchEvent(event)方法处理了。
//如果返回false,那么result也为false。此时就是告诉ViewGroup的dispatchTouchEvent()方法,
//我这个View不处理事件,你另请高明吧,看看其他View有人处理嘛。
//注意这里的处理不处理都只是一个标志而已,并不代表你是否真正处理了事件。比如说我onTouchEvent(event)
//就是返回了false(不处理),但是我里面还是根据ACTION_DOWN做了其他事情。或者我就是返回true,但是我的
//onTouchEvent(event)里面是空代码,什么也没有,直接return true。
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
通过上面的分析我们验证了,上一篇说到的,OnTouchListener的优先级是比onTouchEvent(ev)方法高的。也看到了,如果没有设置OnTouchListener,所有的判断都集中在onTouchEvent(ev)方法里。
所以直接上onTouchEvent(ev)的源码。
public boolean onTouchEvent(MotionEvent event) {
//首先判断这个View是否可点击 CLICKABLE ,是否可长按点击 LONG_CLICKABLE
//如果有一个成立 clickable都为true。否则为false。
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//接下来判断了这个View是否是 DISABLED状态。
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
//这里我们可以看到。即使View当前处于DISABLED状态,只要View可点击和可长按点击
//这里都return了true。也就是说,DISABLED状态也消耗了事件,只是没有对事件做任何处理。
return clickable;
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_DOWN:
//先简答的理解下.
//ACTION_DWON传进来后,就是告诉这个View 那个地方被按压了。并且return true
setPressed(true, x, y);
break;
case MotionEvent.ACTION_UP:
//还记得我们上一篇说是用点击事件举例嘛。一个ACITON_DOWN和一个ACTION_UP
//当ACTION_UP事件也通过上面的流程传进来后。执行了这个方法
//这个方法其实是告诉OnClickListener,有个点击事件。 然后return true
//在下面可以看下performClick()的源码
performClick();
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true;
}
return false;
}
这个时候有人可能会说,那我每次想监听点击事件的时候不都得要先设置clickable和long_clickable嘛。这个时候,我们还是用源码来说话。
//可以看到,不管是设置单击事件还是长按事件,android源码都会为我们自动setClickable(true)和
//setLongClickable(true),所以我们大可不必过分在乎之前监听点击事件的时候,是否设置过这两个属性。
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnLongClickListener = l;
}
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
//判断当前View是否设置了OnClickListener。
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//如果设置了OnClickListener那么直接回调onClick()方法。
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
OK,至此,一个点击事件的分发和处理,我们就通过源码的方式分析完毕。
不过,事件分发不仅如此,还有更深入的问题等着我们探讨。
如有问题,欢迎指正。