當(dāng)一個(gè)點(diǎn)擊事件產(chǎn)生后盒揉,它的傳遞過程:Activity->ViewGroup->View。頂級View接收到事件后,就會(huì)按照事件分發(fā)機(jī)制去分發(fā)事件。
第一步:頂級ViewGroup的事件分派——dispatchTouchEvent
touch事件發(fā)生時(shí)抵乓,首先調(diào)用頂級的ViewGroup的dispatchTouchEvent;如下是dispatchTouchEvent的源碼:
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
//如果自身以及子View都不處理touch事件靶衍,傳遞給上一層
return super.dispatchTouchEvent(ev);
}
//自身處理touch事件
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
在dispatchTouchEvent方法第13行中的兩個(gè)判斷條件:
1灾炭、disallowIntercept是指是否禁用掉事件攔截的功能,默認(rèn)是false(可以通過調(diào)用requestDisallowInterceptTouchEvent方法對這個(gè)值進(jìn)行修改)颅眶。
2蜈出、onInterceptTouchEvent方法表示是否攔截事件。所以涛酗,如果頂級ViewGroup的onInterceptTouchEvent返回true铡原,即攔截事件偷厦,則事件交由ViewGroup處理。
第二步:遍歷子View的事件分發(fā)
如果頂級ViewGroup的onInterceptTouchEvent返回false燕刻,即不攔截事件只泼,則事件會(huì)傳遞給它的子元素,子元素的dispatchTouchEvent會(huì)被調(diào)用:在dispatchTouchEvent方法卵洗,在第19行通過一個(gè)for循環(huán)请唱,遍歷了當(dāng)前ViewGroup下的所有子View,然后在第24行判斷當(dāng)前遍歷的View是不是正在點(diǎn)擊的View过蹂,如果是的話就會(huì)進(jìn)入到該條件判斷的內(nèi)部十绑,然后在第29行調(diào)用了該View的dispatchTouchEvent。子元素的dispatchTouchEvent返回true酷勺,事件傳遞給子元素內(nèi)部處理本橙,如果為false,繼續(xù)傳遞給下一個(gè)子元素鸥印,如果所有子元素都沒有消耗事件勋功,事件將傳遞回ViewGroup處理。
第三步:子View的事件分發(fā)
如下是子View的dispatchTouchEvent源碼:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
從源碼中可以看出库说,這兩個(gè)方法都是在View的dispatchTouchEvent中調(diào)用的狂鞋,onTouch優(yōu)先于onTouchEvent執(zhí)行。如果在onTouch方法中通過返回true將事件消費(fèi)掉潜的,onTouchEvent將不會(huì)再執(zhí)行骚揍。同時(shí),onTouch能夠得到執(zhí)行需要兩個(gè)前提條件啰挪,第一mOnTouchListener的值不能為空信不,第二當(dāng)前點(diǎn)擊的控件必須是enable的。
OnClick方法位于onTouchEvent方法中亡呵,所以O(shè)nTouch的優(yōu)先級高于OnClick方法抽活。