在Android中,Touch事件的分發(fā)在WindowManagerService(借助 InputManagerService)負責(zé)采集和分發(fā),在由ViewRootImpl(內(nèi)部有一個mView變量指向View樹的根),負責(zé)控制View樹的UI繪制和事件消息分發(fā)。
當(dāng)輸入設(shè)備可用時,比如觸屏,Linux內(nèi)核在/dev/input/中創(chuàng)建對應(yīng)的設(shè)備節(jié)點。
IMS(inputManagerService),所所做的工作就是監(jiān)聽/dev/input下的所有的設(shè)備節(jié)點,當(dāng)設(shè)備節(jié)點的數(shù)據(jù)時會將數(shù)據(jù)進行加工處理并找到合適的Window(WMS尋找),將輸入事件派發(fā)給他。
事件采集
- 設(shè)備觸摸
- 觸摸電信號
- 電容屏
- 傳感器
- 電路板
- 驅(qū)動
- Linux
- IMS(
inputManagerService)
當(dāng)輸入設(shè)備可用時,比如觸屏,Lunux內(nèi)核會在/dev/input中創(chuàng)建對應(yīng)的設(shè)備節(jié)點,輸入事件所產(chǎn)生的原始信息會被Linux內(nèi)核輸入子系統(tǒng)采集,原始信息由Kernel Space的驅(qū)動層一直傳遞到User Space的接設(shè)備節(jié)點。
事件中轉(zhuǎn)
- WMS(
WindowManagerService)
WMS的職責(zé)之一就是輸入系統(tǒng)的中轉(zhuǎn)站,WMS(WindowManagerService)作為WIndow的管理者,會配合IMS將輸入事件交給合適的Window來處理。
事件分發(fā)(分發(fā))
- ViewRootimpl
ViewRoot中 caiquWindowInputEventReceiver進行具體的事件處理
1:事件分發(fā)的基本概念
- 觸摸事件:Android的觸摸事件主要封裝在
MotionEvent類中 - 事件類型:包括
ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL等.. - 分發(fā)流程:事件從Activity --> ViewGroup --> View的傳遞過程
2:三個核心方法
dispatchTouchEvent(MotionEvent ev)
作用:負責(zé)事件的分發(fā),決定事件是否繼續(xù)傳遞
-
返回值:true表示消費了事件,false表示不處理此事件
- true/攔截:調(diào)用自身的
onTounchEvent - false/不攔截:將事件傳遞給子View
- true/攔截:調(diào)用自身的
執(zhí)行邏輯:通常先調(diào)用
onInterceptTouchEvent判斷是否攔截
onInterceptTouchEvent(MotionEvent ev)
特性:僅ViewGroup擁有此方法
(View沒有)作用:ViewGroup特有方法,用于攔截事件
-
返回值:true表示攔截事件,false表示不攔截(默認)
- true/攔截:不再向子View傳遞
- false/不攔截:默認值
注意:View沒有此方法,無法攔截事件
onTouchEvent(MotionEvent event)
- 作用:處理點擊事件的具體邏輯
- 返回值:true表示消費事件,false表示不處理事件
- 執(zhí)行時機:當(dāng)
dispatchTouchEvent傳遞到當(dāng)前View時被調(diào)用 - 特殊情況:如果沒有子View消費事件,最終會調(diào)用此方法
//事件分發(fā)機制流程
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) { // 判斷是否攔截
// 攔截后調(diào)用自己的onTouchEvent
consume = onTouchEvent(ev);
} else {
// 不攔截則分發(fā)給子View
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
Activity的事件分發(fā)
- 入口:事件首先傳遞給Activity的dispatchTouchEvent
- 傳遞:Activity將事件傳遞給Window,再傳遞給DecorView
- 最終:事件到達根ViewGroup開始向下分發(fā)
ViewGroup的事件分發(fā)特點
- 遍歷子View:按Z軸順序遍歷子View檢查能否接受點擊
- 坐標(biāo)轉(zhuǎn)換:將事件坐標(biāo)轉(zhuǎn)換為子View的相對坐標(biāo)
- 優(yōu)先處理:如果有子View設(shè)置了TouchListener,會優(yōu)先處理
關(guān)鍵差異對比
| 函數(shù) | ViewGroup | View |
|---|---|---|
| dispatchTouchEvent | 決定是否分發(fā)給子View | 直接處理事件 |
| onInterceptTouchEvent | 存在,可攔截事件 | 不存在 |
| onTouchEvent | 處理未被分發(fā)的事件 | 處理自身事件 |
事件分發(fā)流程

- Activity → Window → DecorView(ViewGroup)
- ViewGroup 調(diào)用 dispatchTouchEvent
- ViewGroup 調(diào)用 onInterceptTouchEvent 判斷是否攔截
- 如不攔截,則傳遞給相應(yīng)子View
- 子View調(diào)用自身的 dispatchTouchEvent 和 onTouchEvent
- 如攔截,則調(diào)用自身的 onTouchEvent
事件的內(nèi)部攔截法和外部攔截法
一:外部攔截法(推薦)
基本原理:
- 在父容器(ViewGroup)的
onInterceptTouchEvent方法中進行攔截控制 - 通過判斷滑動方向或條件來決定是否攔截事件
// 父容器實現(xiàn)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
// DOWN事件不能攔截,否則后續(xù)事件都無法接收到
return false;
}
// 根據(jù)滑動方向判斷是否攔截
if (needIntercept()) {
return true; // 攔截事件,父容器處理
}
return false; // 不攔截,子View處理
}
private boolean needIntercept() {
// 根據(jù)滑動距離、方向等條件判斷
return Math.abs(deltaX) > Math.abs(deltaY);
}
特點:
- DOWN事件不能攔截:必須返回false,保證父容器能接收到后續(xù)事件
- 控制簡單:只需在父容器中處理攔截邏輯
- 符合默認機制:與Android事件分發(fā)機制一致
二:內(nèi)部攔截法
基本原理:
- 子View通過
requestDisallowInterceptTouchEvent方法控制父容器是否攔截 - 子View主動告知父容器不要攔截事件
// 子View實現(xiàn)
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 請求父容器不要攔截
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (isScrollToTop()) {
// 如果滑動到頂部,允許父容器攔截
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.dispatchTouchEvent(ev);
}
// 父容器默認不攔截除了DOWN以外的所有事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return true; // DOWN事件必須攔截
}
return false; // 其他事件默認不攔截
}
特點:
- 需要協(xié)調(diào)配合:子View和父容器都需要參與處理
- 靈活性高:子View可以更精確地控制事件處理
- 復(fù)雜度較高:需要在多個地方進行處理
注意事項
- DOWN事件處理:無論哪種方法,ACTION_DOWN 事件都不能被攔截(外部攔截法)或需要特殊處理(內(nèi)部攔截法)
- 事件完整性:同一事件序列(DOWN-MOVE-UP)應(yīng)由同一個View處理
- 性能考慮:避免在攔截判斷中進行耗時操作