最近在做一個圖片,文字拖拽縮放的控件,所以研究了下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。
第一次寫的有點亂