前言
事件分發(fā)機(jī)制主要是為了處理用戶觸摸屏幕產(chǎn)生的事件雹锣,因?yàn)轫?yè)面中所呈現(xiàn)的是多個(gè)view組合而成网沾,那么具體由誰(shuí)來處理這個(gè)事件就是個(gè)問題
知識(shí)點(diǎn)鋪墊
觸摸屏幕產(chǎn)生的事件(也就是事件分發(fā)的具體對(duì)象)
即MotionEvent是一個(gè)用于報(bào)告運(yùn)動(dòng)的事件,它包含觸摸的動(dòng)作蕊爵、相對(duì)/絕對(duì)位置等
觸摸狀態(tài) | 釋義 |
---|---|
ACTION_DOWN | 按下 手勢(shì)開始 |
ACTION_UP | 抬起 手勢(shì)結(jié)束 |
ACTION_CANCLE | 取消 當(dāng)前手勢(shì)已終止 |
ACTION_MOVE | 移動(dòng) 在down和up之間發(fā)生 |
消費(fèi)事件的對(duì)象
Activity,ViewGroup,View 事件基本上就是從外向里再向外
消費(fèi)事件的對(duì)象組成
怎么消費(fèi)
這三個(gè)消費(fèi)event的對(duì)象由dispatchTouchEvent() 辉哥、onInterceptTouchEvent()和onTouchEvent()這三個(gè)方法處理整個(gè)流程
- 流程圖結(jié)合源碼更容易理解
結(jié)合源碼
一切的源頭:Activity#dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//開始向下一級(jí)分發(fā)
// 會(huì)調(diào)用到Window的實(shí)現(xiàn)類PhoneWindow中
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//上面輪回一圈沒人處理 就自己消費(fèi)了
return onTouchEvent(ev);
}
PhoneWindow#superDispatchTouchEvent()
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//mDecor 就是DecorView
//DecorView本身繼承自FrameLayout 這里調(diào)用的就是ViewGroup的 dispatchTouchEvent()
return mDecor.superDispatchTouchEvent(event);
}
ViewGroup#dispatchTouchEvent()
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/* 略過一些 */
// 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);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
//循環(huán)遍歷子view 并通過dispatchTransformedTouchEvent()方法決定事件走向
for (int i = childrenCount - 1; i >= 0; i--) {
...
//獲取到子view dispatch
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
...
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
//此處mFirstTouchTarget給賦值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
//上面分發(fā)后子View沒有處理的話 此處就為null
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
} else {
...
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
...
}
//子視圖空調(diào)用父布局
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
}
return handled;
}
真正的分發(fā):ViewGroup#dispatchTransformedTouchEvent()
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
//有子View就繼續(xù)向下調(diào)用 沒有就調(diào)用自身的
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
}
View.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
...
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//上面判斷了是否設(shè)置了onTouch事件 如果有就不會(huì)回調(diào)onTouchEvent事件
result = true;
}
//調(diào)用了自身的onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
View.onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// 當(dāng)處于按下狀態(tài)時(shí)才有點(diǎn)擊事件
if (!focusTaken) {
// 使用一個(gè)Runnable發(fā)送點(diǎn)擊事件
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
...
}
break;
case MotionEvent.ACTION_DOWN:
...
if (!clickable) {
//檢測(cè)是否長(zhǎng)按事件 通過Runnable
checkForLongClick(0, x, y);
break;
}
...
break;
case MotionEvent.ACTION_CANCEL:
//重置/刪除大部分狀態(tài)
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
return true;
}
return false;
}
總結(jié)
- 事件起始由ACTION_DOWN 結(jié)束為ACTION_UP
- 整個(gè)流程是責(zé)任鏈模式 從上到下再到上(okhttp中攔截器也是這樣)
- 事件傳遞優(yōu)先級(jí):onTouchListener.onTouch -> onTouchEvent -> onClickListener.onClick
- ViewGroup默認(rèn)不攔截任何事件(返回false)
最后
- 之前看了很多次關(guān)于這方面的知識(shí),但總是感覺斷斷續(xù)續(xù)攒射,模模糊糊醋旦,所以這次做一個(gè)小小的總結(jié),留給以后回顧会放,如果文中如果有什么紕漏歡迎討論與指出浑度。
- 參考
Android觸摸事件傳遞機(jī)制
Android事件分發(fā)機(jī)制詳解:史上最全面、最易懂
深入理解Android事件分發(fā)機(jī)制
圖解 Android 事件分發(fā)機(jī)制