寫(xiě)在前面
首先要了解事件分發(fā)機(jī)制的傳遞流程是在點(diǎn)擊事件發(fā)生后,事件先傳遞到Activity深浮,然后傳遞到ViewGroup剩辟,最終傳遞到View。
主要涉及的三個(gè)方法分別為:
dispatchTouchEvent():分發(fā)傳遞點(diǎn)擊事件
onInterceptTouchEvent() :判斷是否攔截了點(diǎn)擊事件
onTouchEvent() : 處理點(diǎn)擊事件
源碼分析完后做出事件分發(fā)的流程圖
源碼分析
1. Activity的事件傳遞機(jī)制
查看自定義Activity中的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//系統(tǒng)默認(rèn)點(diǎn)擊空白處 super.dispatchTouchEvent(ev)=false
//系統(tǒng)默認(rèn)點(diǎn)擊按鈕處 super.dispatchTouchEvent(ev)=true
//必須執(zhí)行super.dispatchTouchEvent(ev) 方法 否則即使返回true或者false 都不會(huì)往下傳遞也不會(huì)執(zhí)行onTouchEvent()
return super.dispatchTouchEvent(ev);
}
查看Activity.java$dispatchTouchEvent方法源碼
public boolean dispatchTouchEvent(MotionEvent ev) {
//ev.getAction()獲取屏幕的點(diǎn)擊事件類型躯保,ACTION_DOWN為按下事件
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 該方法為空旋膳,作用為實(shí)現(xiàn)屏幕保護(hù)功能
onUserInteraction();
}
//getWindow()方法獲取的是一個(gè)Window對(duì)象
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//返回處理點(diǎn)擊事件的結(jié)果
return onTouchEvent(ev);
}
因?yàn)閃indow是一個(gè)抽象類其具體實(shí)現(xiàn)類為PhoneWindow。
查看PhoneWindow.java$superDispatchTouchEvent方法源碼
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor是DecorView的一個(gè)對(duì)象吻氧,而DecorView是Window的頂層View也是我們看到的整個(gè)顯示界面溺忧。DecorView繼承FrameLayout類,而FrameLayout是ViewGroup子類盯孙。
查看DecorView.java$superDispatchTouchEvent方法源碼
public boolean superDispatchTouchEvent(MotionEvent event) {
//上面說(shuō)到DecorView是ViewGroup的間接子類鲁森,
//這里調(diào)用的便是ViewGroup的dispatchTouchEvent方法,后面會(huì)分析ViewGroup的該方法
return super.dispatchTouchEvent(event);
}
查看自定義Activity中onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
//系統(tǒng)默認(rèn)super.onTouchEvent(event) = false
//無(wú)論true或者false 都會(huì)執(zhí)行該方法中的代碼
return super.onTouchEvent(event);
}
查看Activity.java$onTouchEvent方法源碼
public boolean onTouchEvent(MotionEvent event) {
//處理發(fā)生在Window邊界外的點(diǎn)擊事件
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
//查看Window.java$shouldCloseTouch方法源碼
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
//判斷點(diǎn)擊事件以及event的x振惰,y軸坐標(biāo)是否在邊界外
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
//若事件在邊界外則消費(fèi)該事件返回true
return true;
}
return false;
}
2. ViewGroup的事件分發(fā)機(jī)制
查看自定義ViewGroup的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//系統(tǒng)默認(rèn)super.dispatchTouchEvent(ev) = true
//返回true繼續(xù)往下分發(fā) 若返回false 停止事件分發(fā)
//且onInterceptTouchEvent 歌溉,onTouchEvent將不會(huì)執(zhí)行
return super.dispatchTouchEvent(ev);
}
查看ViewGroup.java$dispatchTouchEvent方法源碼
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 對(duì)輸入框的統(tǒng)一驗(yàn)證,不為null 則對(duì)輸入框的點(diǎn)擊事件進(jìn)行處理
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
//以應(yīng)用安全策略過(guò)濾觸摸事件,返回true即繼續(xù)分發(fā)事件
if (onFilterTouchEventForSecurity(ev)) {
//獲取事件點(diǎn)擊類型
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
//處理按下事件
if (actionMasked == MotionEvent.ACTION_DOWN) {
//取消并清除所有與觸摸目標(biāo)之間的聯(lián)系
cancelAndClearTouchTargets(ev);
//重置所有觸摸狀態(tài)準(zhǔn)備新的循環(huán)
resetTouchState();
}
final boolean intercepted;
// 對(duì)事件是否攔截進(jìn)行處理
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//是否允許攔截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//允許攔截
if (!disallowIntercept) {
// 內(nèi)部調(diào)用onInterceptTouchEvent事件攔截方法
intercepted = onInterceptTouchEvent(ev);
//恢復(fù)action防止事件發(fā)生變化
ev.setAction(action);
} else {
//不允許攔截 intercept 設(shè)置為false
intercepted = false;
}
} else {
//沒(méi)有觸摸目標(biāo) 且ation沒(méi)有初始化down事件
//則ViewGroup將會(huì)繼續(xù)攔截分發(fā)事件
intercepted = true;
}
......//代碼省略
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果沒(méi)有取消觸摸事件以及攔截點(diǎn)擊事件,則繼續(xù)分發(fā)
if (!canceled && !intercepted) {
......//代碼省略
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
//移除早期的觸摸目標(biāo)id防止它們不能同步
removePointersFromTouchTargets(idBitsToAssign);
//mChildrenCount為mChildren數(shù)組中有效的子元素?cái)?shù)量
//mChildren為ViewGroup中的子視圖View
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 查找收到觸摸事件的View
// 返回觸摸被分發(fā)的自定義排列視圖
......//代碼省略
final View[] children = mChildren;
//遍歷當(dāng)前ViewGroup中的有效視圖
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
......//代碼省略
//該方法處理子View的dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {}
}
}
......//代碼省略
return handled;
}
//繼續(xù)查看dispatchTransformedTouchEvent方法核心源碼
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
......//代碼省略
//調(diào)用View的dispatchTouchEvent(后面會(huì)分析View該方法)
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
......//代碼省略
return handled;
}
接著查看自定義ViewGroup的onInterceptTouchEvent方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//系統(tǒng)默認(rèn)super.onInterceptTouchEvent(ev) = false
//返回true則對(duì)事件進(jìn)行攔截痛垛,且onTouchEvent事件繼續(xù)執(zhí)行
return super.onInterceptTouchEvent(ev);
}
//繼續(xù)分析ViewGroup.java$onInterceptTouchEvent方法
//是否攔截事件
public boolean onInterceptTouchEvent(MotionEvent ev) {
//返回true 即攔截事件草慧,事件停止往下傳遞需要復(fù)寫(xiě)onInterceptTouchEvent()方法
//系統(tǒng)默認(rèn)返回false,不攔截事件
return false;
}
查看自定義ViewGroup的onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
//系統(tǒng)默認(rèn)onTouchEvent() = true
//返回false 只影響自身的onClickListener事件
return super.onTouchEvent(event);
}
因?yàn)閂iewGroup是繼承View匙头,這里調(diào)用onTouchEvent方法也就是調(diào)用View類中的onTouchEvent方法(后面會(huì)分析該方法源碼)漫谷。
3. View的事件分發(fā)機(jī)制
首先查看自定義View的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
//系統(tǒng)默認(rèn)super.dispatchTouchEvent(event) =true
//若返回false 將不再分發(fā)事件也不會(huì)執(zhí)行onTouchEvent事件
return super.dispatchTouchEvent(event);
}
//繼續(xù)查看View.java$dispatchTouchEvent方法核心源碼
public boolean dispatchTouchEvent(MotionEvent event) {
......//代碼省略
boolean result = false;
......//代碼省略
if (onFilterTouchEventForSecurity(event)) {
//mViewFlags為視圖標(biāo)識(shí)記錄視圖狀態(tài)
//(mViewFlags & ENABLED_MASK) == ENABLED 判斷
//該點(diǎn)擊控件是否是enabled 類型 默認(rèn)該條件為true
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//mListenerInfo 各類監(jiān)聽(tīng)器信息
ListenerInfo li = mListenerInfo;
//條件1:li != null
//條件2:li.mOnTouchListener != null
//(mViewFlags & ENABLED_MASK) == ENABLED 默認(rèn)為true
//條件3:li.mOnTouchListener.onTouch(this, event)設(shè)置onTouch事件需要重寫(xiě)onTouch方法
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//若onTouch方法返回false則執(zhí)行onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
......//代碼省略
return result;
}
先分析條件2:li.mOnTouchListener != null查找mOnTouchListener的賦值代碼
public void setOnTouchListener(OnTouchListener l) {
//若注冊(cè)onTouch事件則mOnTouchListener 永不為null
getListenerInfo().mOnTouchListener = l;
}
//繼續(xù)分析getListenerInfo方法
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
//該方法為mListenerInfo賦值也就是說(shuō)條件1:li != null一直為true
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
查看自定義View類中的onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
//系統(tǒng)默認(rèn)super.onTouchEvent() = true
//返回false 則不再執(zhí)行自身點(diǎn)擊事件
return super.onTouchEvent(event);
}
//查看View.java$onTouchEvent方法核心源碼
public boolean onTouchEvent(MotionEvent event) {
......//代碼省略
//若view可點(diǎn)擊則進(jìn)入switch語(yǔ)句判斷該事件類型并執(zhí)行對(duì)應(yīng)代碼
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
//松開(kāi)按下的view
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
......//省略各種判斷代碼執(zhí)行performClick方法
performClick();
//按下View
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break;
// 取消觸摸事件
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
//滑動(dòng)view
case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y);
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
//若view可點(diǎn)擊則返回true
return true;
}
//若view不可點(diǎn)擊則返回false
return false;
}
查看View.java$performClick方法源碼
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//調(diào)用onClick方法
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
分析完View的dispatchTouchEvent方法可以發(fā)現(xiàn)該方法中先執(zhí)行onTouch方法,若onTouch方法返回值為false蹂析,則執(zhí)行OnTouchEvent方法舔示,然后執(zhí)行該方法中performClick方法,最后再調(diào)用onclick方法电抚。
圖片摘取地址