最近在做一个图片,文字拖拽缩放的控件,所以研究了下android的事件传递机制,记录在此。
一、Activity——dispatchTouchEvent和onTouchEvent
先来看下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),getWindow()返回的是一个PhoneWindow对象,执行了superDispatchTouchEvent(ev)方法,如果返回true表示事件被消费掉。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor是一个DecorView对象,继承自FrameLayout,它是一个Activity的root view,通过super.dispatchTouchEvent会把事件分发给各个子view及Activity onCreate方法中setContentView里赋值的view。
二、ViewGroup——dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent
// Check for interception.
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(ev)里代码很简单就返回了个false,disallowIntercept用来控制是否禁用拦截事件,可 以通过 requestDisallowInterceptTouchEvent方法改变值。如果事件没被拦截会执行下面的代码:
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
遍历了所有子view,
找到点击的view执行dispatchTransformedTouchEvent方法:
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
可以看到child.dispatchTouchEvent(event),所以ViewGroup的
dispatchTouchEvent依次调用了子view的dispatchTouchEvent方法,当子view的dispatchTouchEvent方法返回true时事件到此结束,返回false时会去执行super.dispatchTouchEvent方法,也就是父类View的dispatchTouchEvent方法,下面介绍View的dispatchTouchEvent方法;
三、View——dispatchTouchEvent、onTouchEvent
先来看dispatchTouchEvent:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
判断了mOnTouchListener是否为空和它的返回值,如果不为空并且返回true,dispatchTouchEvent直接返回true,否则执行onTouchEvent(event),
onTouchEvent的默认返回值与CLICKABLE和LONG_CLICKABLE属性有关,例如button默认返回true,而textview默认返回false。如果
onTouchEvent返回true及
dispatchTouchEvent返回true时间传递结束,否则会执行父布局的
onTouchEvent。
第一次写的有点乱