上篇文章《快速了解Android6.0系统触摸事件工作原理——InputManagerService》分析到dispatchTouchEvent()方法来做分发事件的处理,这个分发过程分为两种情况:(1)若目前的View是普通的View,就会调用View.java的dispatchTouchEvent()方法来处理。普通的View,比如TextView里面并没有实现dispatchTouchEvent()方法,由于它又是继承View,所有在View的dispatchPointerEvent()方法中调用dispatchTouchEvent()方法或者进入View的dispatchTouchEvent()方法。(2)若目前的View是容器类View,比如DecorView,由于它实现了dispatchTouchEvent()方法,所以View的dispatchPointerEvent()方法中调用dispatchTouchEvent()方法就会进入DecorView的dispatchTouchEvent()方法。
1、普通View事件分发
/frameworks/base/core/java/android/view/view.java
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;
}
}
...
}
View的dispatchTouchEvent()方法先判断当前是不是有mOnTouchListener,如有就调用它的onTouch()方法,若没有就会到if中调用onTouchEvent()方法,如若子类没有去实现onTouchEvent()方法,这个调用就进入View的onTouchEvent()方法中
/frameworks/base/core/java/android/view/view.java
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//首先得到event的坐标x,y的值
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP: //若action为抬起
..
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
...
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
...
if (!focusTaken) {
...
if (!post(mPerformClick)) {
performClick();
}
}
}
...
break;
case MotionEvent.ACTION_DOWN: //按下动作
..
break;
case MotionEvent.ACTION_CANCEL: //取消动作
...
break;
case MotionEvent.ACTION_MOVE: //Move动作
...
break;
}
return true;
}
return false;
}
View的onTouchEvent()方法返回值,返回的true,表示已经处理event;返回的false,表示没有处理,在方法中真正实现的是对UP、Down、Move事件的处理。以UP事件为例,分析这个case经过多个if判断最后调用performClick()方法的现象。
/frameworks/base/core/java/android/view/view.java
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
performClick()方法中的变量mOnClickListener是一个OnClickListener对象,在应用程序开发中的View,比如Button,添加这个按钮事件时,代码如下:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
//做具体的工作内容
}
});
上面代码new一个OnClickListener对象并且通过setOnClickListener()方法设置到View中,所以performClick()方法中的mOnClickListener就从应用程序开发中设置。在View的performClick()方法中,调用了 li.mOnClickListener中的onClick(this)方法,这样就进入到应用程序开发中的onClick()方法,就如上面的button例子,最后进入到了button的onClick()方法中。
2、容器类View事件分发
以View是DecorView为例,View中的dispatchPointerEvent()方法调用dispatchTouchEvent()方法之后会进入DecorView的dispatchTouchEvent()方法。
/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev): super.dispatchTouchEvent(ev);
}
...
}
}
DecorView类中的dispatchTouchEvent()方法通过调用getCallback()方法返回Window.Callback接口对象,Activity的attach()方法会把Activity的callback设置到window中,因此getCallback()方法获得目前的Activity对象。isDestroyed()方法用于判断Activity是否destroy,mFeatureId < 0用于判断目前的view是否是根视图DecorView。因为目前case是根视图对象,所以cb != null && !isDestroyed() && mFeatureId < 0为true,于是调用cb.dispatchTouchEvent()方法进入到Activity中。
/frameworks/base/core/java/com/android/app/Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Activity的dispatchTouchEvent()方法首先判断Action是否是Down,若是,就调用onUserInteraction()方法,接着通过getWindow()方法获得PhoneWindow对象,并调用它的superDispatchTouchEvent()方法,返回true,表示目前已找到View来处理事件,就不需要Activity来处理事件了,也不调用Activity的onTouchEvent()方法,否则,getWindow().superDispatchTouchEvent(ev)返回false,表示需要Activity来处理,此时需要调用它的onTouchEvent()方法来进一步处理事件。
/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
...
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
...
}
...
}
变量mDecor是PhoneWindow中的一个DecorView类型的全局变量,在PhoneWindow中的superDispatchTouchEvent()方法中会调用它的superDispatchTouchEvent()方法。而superDispatchTouchEvent()方法又直接调用其父类型的dispatchTouchEvent()方法来就一步操作。DecorView类继承了FrameLayout类,并且FrameLayout类并没有实现dispatchTouchEvent()方法,实现这个方法是容器类ViewGroup的任务。由于FrameLayout类继承ViewGroup类,因此就调用了ViewGroup类的dispatchTouchEvent()方法。
/frameworks/base/core/java/android/view/ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {
...
if (onFilterTouchEventForSecurity(ev)) {
//判断这个event是否符合安全策略
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) { //清除以往的Touch状态
cancelAndClearTouchTargets(ev); //把mFirstTouchTarget设置为null
resetTouchState(); //充值Touch状态标识
}
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);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
...
if (!canceled && !intercepted) {
...
//处理ACTION_DOWN事件
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
final int childrenCount = mChildrenCount;
//得到子View的个数
if (newTouchTarget == null && childrenCount != 0) {
//根据Touch坐标找出子View来接收Touch事件
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
...
//遍历子View查找子View接收Touch事件
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
...
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) { //接收Touch事件的子View已经被找到
newTouchTarget.pointerIdBits |= idBitsToAssign;
break; //跳出循环
}
resetCancelNextUpFlag(child);
//若不满足上面的if,就进入这个if,将Touch事件传递给子View做递归处理,即遍历该子View的所有子View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
}
...
}
ViewGroup的dispatchTouchEvent()方法调用了dispatchTransformedTouchEvent()方法将事件分发到子View中。
/frameworks/base/core/java/android/view/ViewGroup.java
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
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);
}
...
}
在上面代码中,若变量child为空,就调用其父类的dispatchTouchEvent()方法来处理,若变量child不为空,就调用其dispatchTouchEvent()方法处理。由于child是View对象,因此这个调用又回到前面View分发事件的起始处dispatchTouchEvent()方法。依次类推,当child是一个ViewGroup,就继续这样分发下去,当child是一个普通的View,就调用View的dispatchTouchEvent()方法来处理。
总结
在View的dispatchPointerEvent()方法中,如果View是普通的View,即View没有实现dispatchTouchEvent()方法,那么分发时就调用它的dispatchTouchEvent()方法来处理;如果View是ViewGroup或者ViewGroup的子类,它们实现了dispatchTouchEvent()方法,分发时就调用它们的dispatchTouchEvent()方法来处理。