View事件分發(fā)
InputDispatcher通過(guò)與對(duì)應(yīng)窗口建立連接通道,將事件信息封裝成InputMessgae,通過(guò)InputChannel將信息發(fā)送到窗口端socket,looper監(jiān)聽(tīng)到fd有數(shù)據(jù)寫入,執(zhí)行NativeInputEventReceiver::handleEvent函數(shù),讀取窗口socket端數(shù)據(jù),在返回到j(luò)ava端進(jìn)行處理
InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
執(zhí)行InputEventReceiver的重寫函數(shù)onInputEvent
在ViewRootImpl中定義了InputEventReceiver的子類WindowInputEventReceiver
ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
List<InputEvent> processedEvents;
try {
processedEvents =
mInputCompatProcessor.processInputEventForCompatibility(event);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (processedEvents != null) {
if (processedEvents.isEmpty()) {
// InputEvent consumed by mInputCompatProcessor
finishInputEvent(event, true);
} else {
for (int i = 0; i < processedEvents.size(); i++) {
enqueueInputEvent(
processedEvents.get(i), this,
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
}
}
} else {
enqueueInputEvent(event, this, 0, true);
}
}
1 創(chuàng)建processedEvents集合
2 processInputEventForCompatibility考慮在發(fā)送app前做適配
3 判斷適配處理后的事件是否存在
不存在代表事件處理完畢 將事件反饋給到InputDispatcher
存在 遍歷processedEvents執(zhí)行enqueueInputEvent入隊(duì)派發(fā)
4 適配處理后的事件不存在 代表當(dāng)前事件不需要適配,直接執(zhí)行enqueueInputEvent入隊(duì)派發(fā)
ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
if (event instanceof MotionEvent) {
MotionEvent me = (MotionEvent) event;
if (me.getAction() == MotionEvent.ACTION_CANCEL) {
EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Motion - Cancel",
getTitle().toString());
}
} else if (event instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) event;
if (ke.isCanceled()) {
EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Key - Cancel",
getTitle().toString());
}
}
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
1 將event receiver flag 封裝為QueuedInputEvent
2 判斷event能否轉(zhuǎn)為MotionEvent
3 判斷MotionEvent.action是MotionEvent.ACTION_CANCEL
EventLog寫入cancel事件
4 判斷KeyEvent.action是KeyEvent.isCanceled
EventLog寫入cancel事件
5 input事件隊(duì)列追加事件
6 這里processImmediately為true 執(zhí)行doProcessInputEvents
ViewRootImpl.java
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
1 從緩存事件隊(duì)列頭部取出事件
2 緩存事件數(shù)量-1
3 deliverInputEvent 分發(fā)事件
ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getId());
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x"
+ Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano="
+ q.mEvent.getEventTimeNano() + " id=0x"
+ Integer.toHexString(q.mEvent.getId()));
}
try {
if (mInputEventConsistencyVerifier != null) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "verifyEventConsistency");
try {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
try {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
1 驗(yàn)證輸入事件的一致性。如果事件存在異常,則會(huì)拋出異常并終止程序的執(zhí)行
2 輸入事件的處理流程包括多個(gè) Stage,每個(gè) Stage 負(fù)責(zé)一些特定的任務(wù),shouldSendToSynthesizer用于處理,mSyntheticInputStage主要負(fù)責(zé)處理需要進(jìn)行輸入法合成的輸入事件
3 q.shouldSendToSynthesizer() false 代表不是合成的輸入事件,根據(jù)shouldSkipIme確認(rèn)選擇mFirstPostImeInputStage 處理輸入法事件還是mFirstInputStage處理輸入事件的第一階段
4 判斷mEvent的類型是否為KeyEvent,是的話執(zhí)行 mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
5 上述選擇的stage不為空,檢查窗口焦點(diǎn)是否發(fā)生變化來(lái)調(diào)整事件的派發(fā),之后調(diào)用Stage.deliver來(lái)派發(fā)
在研究Stage.deliver派發(fā)事件前要先了解stage的概念
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
1 可以看到在執(zhí)行setView方法
2 構(gòu)建了7個(gè)階段
AsyncInputStage:這個(gè)階段是輸入系統(tǒng)的異步處理階段。在用戶輸入事件發(fā)生后,首先會(huì)經(jīng)過(guò)硬件驅(qū)動(dòng)層的處理,然后傳遞給應(yīng)用程序的主線程。在主線程中,輸入事件會(huì)被封裝并發(fā)送到 AsyncInputStage 進(jìn)行異步處理。這個(gè)階段的任務(wù)包括事件的解析、轉(zhuǎn)換、分發(fā)等操作,確保輸入事件能夠被正確地傳遞給相應(yīng)的目標(biāo)視圖進(jìn)行處理。
第一階段:
SyntheticInputStage 是 Android 系統(tǒng)中的一個(gè)輸入階段,它屬于系統(tǒng)輸入處理的一部分。具體來(lái)說(shuō),SyntheticInputStage 用于處理合成輸入事件。
在 Android 中,合成輸入事件是由系統(tǒng)模擬生成的輸入事件,而不是由用戶實(shí)際觸發(fā)的。合成輸入事件可以用于模擬用戶操作,例如自動(dòng)化測(cè)試、輔助功能等場(chǎng)景。SyntheticInputStage 負(fù)責(zé)接收和處理這些合成輸入事件,并將它們傳遞給目標(biāo)視圖進(jìn)行相應(yīng)的處理。
在 SyntheticInputStage 階段,系統(tǒng)會(huì)將合成輸入事件按照特定的順序進(jìn)行處理,通常包括以下幾個(gè)步驟:
合成輸入事件的生成:系統(tǒng)根據(jù)需要生成合成輸入事件,這些事件可以模擬用戶的點(diǎn)擊、滑動(dòng)、長(zhǎng)按等操作。生成合成輸入事件的方式可以通過(guò)調(diào)用相應(yīng)的 API 或使用相關(guān)的工具類來(lái)實(shí)現(xiàn)。
合成輸入事件的分發(fā):生成的合成輸入事件會(huì)被系統(tǒng)分發(fā)到相應(yīng)的 SyntheticInputStage 階段進(jìn)行處理。系統(tǒng)會(huì)按照一定的規(guī)則確定目標(biāo)視圖,然后將合成輸入事件傳遞給目標(biāo)視圖進(jìn)行處理。
合成輸入事件的處理:SyntheticInputStage 接收到合成輸入事件后,會(huì)將事件傳遞給目標(biāo)視圖進(jìn)行處理。目標(biāo)視圖可以是應(yīng)用程序中的任何視圖,例如按鈕、文本框等。目標(biāo)視圖根據(jù)接收到的合成輸入事件來(lái)執(zhí)行相應(yīng)的操作,如執(zhí)行點(diǎn)擊操作、滾動(dòng)操作等。
需要注意的是,SyntheticInputStage 階段是系統(tǒng)的一部分,一般情況下開發(fā)者無(wú)需直接操作或干預(yù)該階段的過(guò)程。這個(gè)階段在 Android 系統(tǒng)內(nèi)部進(jìn)行,主要目的是處理合成輸入事件,以模擬用戶操作或?qū)崿F(xiàn)輔助功能。
第二階段:
ViewPostImeInputStage 是 Android 系統(tǒng)中的一個(gè)輸入階段,它屬于系統(tǒng)輸入處理的一部分。具體來(lái)說(shuō),ViewPostImeInputStage 用于處理 IME(輸入法)相關(guān)的輸入事件。
在 Android 中,IME 輸入事件是由輸入法軟件發(fā)送給應(yīng)用程序的。當(dāng)用戶在文本框或其他可編輯視圖中進(jìn)行輸入時(shí),輸入法軟件會(huì)生成相應(yīng)的輸入事件,并將其發(fā)送到應(yīng)用程序中。ViewPostImeInputStage 階段負(fù)責(zé)接收和處理這些輸入事件,并將它們傳遞給目標(biāo)視圖進(jìn)行相應(yīng)的處理。
在 ViewPostImeInputStage 階段,系統(tǒng)會(huì)按照以下步驟處理 IME 相關(guān)的輸入事件:
IME 輸入事件的生成:當(dāng)用戶在輸入法軟件中輸入文字時(shí),輸入法軟件會(huì)生成相應(yīng)的輸入事件,如文本變化事件、光標(biāo)位置變化事件等。
IME 輸入事件的分發(fā):輸入法軟件將生成的輸入事件發(fā)送到當(dāng)前活動(dòng)的應(yīng)用程序中。系統(tǒng)會(huì)將這些輸入事件分發(fā)給適當(dāng)?shù)?ViewPostImeInputStage 階段進(jìn)行處理。
IME 輸入事件的處理:ViewPostImeInputStage 接收到 IME 輸入事件后,會(huì)將事件傳遞給目標(biāo)視圖進(jìn)行處理。目標(biāo)視圖通常是當(dāng)前正在與用戶交互的文本框或可編輯視圖。目標(biāo)視圖根據(jù)接收到的輸入事件來(lái)更新文本內(nèi)容、光標(biāo)位置等,并可能觸發(fā)相應(yīng)的監(jiān)聽(tīng)器或回調(diào)方法。
需要注意的是,ViewPostImeInputStage 階段是系統(tǒng)的一部分,開發(fā)者無(wú)需直接操作或干預(yù)該階段的過(guò)程。這個(gè)階段在 Android 系統(tǒng)內(nèi)部進(jìn)行,主要目的是處理與輸入法相關(guān)的輸入事件,以確保用戶的輸入能夠正確地顯示和處理。
第三階段:
nativePostImeStage是在提到原生(native)層面的輸入處理,可以理解為與輸入法相關(guān)的底層操作和事件。
在底層的原生開發(fā)中,涉及到輸入法的處理通常包括以下幾個(gè)方面:
輸入法管理器(InputMethodManager):在原生開發(fā)中,可以使用輸入法管理器來(lái)控制輸入法的顯示、隱藏以及切換。通過(guò)輸入法管理器,可以獲取當(dāng)前的輸入法信息、打開或關(guān)閉輸入法窗口、獲取輸入法視圖等。
輸入法事件處理:原生開發(fā)可以處理與輸入法相關(guān)的事件,例如鍵盤按鍵事件(KeyEvent)、觸摸事件(MotionEvent)等。通過(guò)監(jiān)聽(tīng)和處理這些事件,可以實(shí)現(xiàn)與輸入法交互的功能,如捕獲按鍵輸入、響應(yīng)觸摸操作等。
輸入框處理:原生開發(fā)還可以針對(duì)輸入框進(jìn)行處理,包括輸入框的創(chuàng)建、位置和大小的調(diào)整、內(nèi)容的顯示和編輯等。通過(guò)與輸入框的交互,可以實(shí)現(xiàn)與用戶的輸入交互和數(shù)據(jù)處理。
第四階段:
接下來(lái)我們看其中一個(gè)stage的構(gòu)造函數(shù)
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
這里可以看到將ViewPostImeInputStage關(guān)聯(lián)的下一階段stage,放到父類InputStage的構(gòu)造函數(shù)中
public InputStage(InputStage next) {
mNext = next;
}
這里看到下個(gè)階段的stage賦值到mNext中
那么在執(zhí)行父類InputStage的方法時(shí)比如
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
這里調(diào)用 mNext.deliver(q)就來(lái)到了下個(gè)階段的deliver函數(shù)中
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
traceEvent(q, Trace.TRACE_TAG_VIEW);
final int result;
try {
result = onProcess(q);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
apply(q, result);
}
}
在這里執(zhí)行下一個(gè)階段的onProcess函數(shù),將處理結(jié)果和事件給到
apply(q, result)
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
這里根據(jù)處理結(jié)果執(zhí)行不同函數(shù) 關(guān)于apply來(lái)說(shuō)由于stage不同分為了同步異步倆個(gè)部分 這里主要看同步
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
執(zhí)行onDeliverToNext方法
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
這里可以看到要分發(fā)到下下個(gè)階段了,同時(shí)如果所有階段都執(zhí)行完就執(zhí)行finishInputEvent給InputDispatcher反饋了
再看下finish函數(shù)
protected void finish(QueuedInputEvent q, boolean handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
if (handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
}
forward(q);
}
handled為true代表事件處理完成事件加上QueuedInputEvent.FLAG_FINISHED_HANDLED flag ,分發(fā)下個(gè)階段,下個(gè)階段一看事件已經(jīng)處理完了,那直接跳過(guò)平臺(tái)來(lái)給InputDispatcher反饋
經(jīng)過(guò)AsyncInputStage ViewPostImeInputStage階段
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
1 當(dāng)前是KeyEvent執(zhí)行processKeyEvent
2 source是InputDevice.SOURCE_CLASS_POINTER 執(zhí)行processPointerEvent
3 source是InputDevice.SOURCE_CLASS_TRACKBALL執(zhí)行processTrackballEvent事件
4 其他執(zhí)行processGenericMotionEvent
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mHandwritingInitiator.onTouchEvent(event);
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
1 獲取event事件
2 執(zhí)行 mView.dispatchPointerEvent(event)
3 根據(jù)結(jié)果確認(rèn)當(dāng)前是FINISH_HANDLED還是FORWARD
View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
1 檢查事件是否為touch事件 是的話執(zhí)行DecorView.dispatchTouchEvent
DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
1 獲取phoneWindow的callback對(duì)象即窗口的Activity
2 執(zhí)行Activity的dispatchTouchEvent方法
關(guān)于為什么phoneWindow的callback對(duì)象即窗口的Activity
Activity.java
final void attach(......){
......
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this); // 這里的 this 就是 Activity
......
}
接下來(lái)執(zhí)行Activity的dispatchTouchEvent方法
Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
1 首先ACTION_DOWN事件執(zhí)行onUserInteraction這個(gè)空方法,通知用戶,Activity已經(jīng)接收到事件了
2 當(dāng)前Activity的窗口執(zhí)行superDispatchTouchEvent方法,派發(fā)成功返回true
3 如果上述都失敗了,就執(zhí)行Activity的onTouchEvent方法
接下來(lái)看當(dāng)前Activity的窗口執(zhí)行superDispatchTouchEvent方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
1 執(zhí)行DecorView的superDispatchTouchEvent方法
接下來(lái)看DecorView的superDispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
1 執(zhí)行父類的superDispatchTouchEvent方法,這里的super是ViewGroup
為什么這里的super是ViewGroup
public class DecorView extends FrameLayout
public class FrameLayout extends ViewGroup
1 可以看到最終的父類是ViewGroup
在查看ViewGroup的dispatchTouchEvent方法前,先做一個(gè)java端事件派發(fā)總結(jié):
可以看到在輸入事件通過(guò)NativeInputEventReciver的InputConsumer從InputChannel中讀出由JNI ->java端 ->ViewRootImpl -> InputStage -> DecorView -> Activity -> PhoneWindow -> DecorView -> ViewGroup
接下來(lái)看ViewGroup的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
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;
// 判斷此次觸摸事件是否被過(guò)濾掉,條件由兩個(gè) flag 決定,F(xiàn)ILTER_TOUCHES_WHEN_OBSCURED
// 和 MotionEvent.FLAG_WINDOW_IS_OBSCURED,這兩個(gè) flag 用來(lái)表示
// 當(dāng)前接收觸摸事件的 View 是否被遮擋或者隱藏,只有未被遮擋或隱藏才能
// 進(jìn)一步處理事件
//事件安全檢查
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
//事件action為MotionEvent.ACTION_DOWN代表一次新的事件序列到來(lái),需要清空上一次
//事件序列的相關(guān)狀態(tài)
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//Action為ACTION_DOWN或者mFirstTouchTarget(接收事件的目標(biāo))不為空
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//檢查mGroupFlags是否為FLAG_DISALLOW_INTERCEPT
//FLAG_DISALLOW_INTERCEPT這個(gè)為不允許攔截事件Flag
if (!disallowIntercept) {
//父view調(diào)用onInterceptTouchEvent攔截事件
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
//設(shè)置intercepted(父View攔截Flag為false)
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
//當(dāng)前Action不是ACTION_DOWN或者沒(méi)有mFirstTouchTarget(接收事件的目標(biāo))
//事件要被攔截
intercepted = true;
}
// Check for cancelation.
//resetCancelNextUpFlag(this)為true或者Action為ACTION_CANCEL
//會(huì)設(shè)置canceled為true
//resetCancelNextUpFlag方法指的View是否存在PFLAG_CANCEL_NEXT_UP_EVENT標(biāo)志
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
//是否為鼠標(biāo)事件
final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
//mGroupFlags是否包含F(xiàn)LAG_SPLIT_MOTION_EVENTS并且不是鼠標(biāo)事件
//檢查View是否支持事件拆分
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0
&& !isMouseEvent;
//定義新的接收事件的目標(biāo)
TouchTarget newTouchTarget = null;
//事件是否派發(fā)到新的接收事件的目標(biāo)的標(biāo)志位
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
//進(jìn)入該判斷的事件 未被取消并且攔截
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//新事件序列開始的行為則進(jìn)入該判斷
//獲取事件Index
final int actionIndex = ev.getActionIndex(); // always 0 for down
//根據(jù)是否支持事件拆分獲取觸控點(diǎn)位
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
//移除上次事件序列的觸控點(diǎn)位
removePointersFromTouchTargets(idBitsToAssign);
//獲取子視圖數(shù)量
final int childrenCount = mChildrenCount;
//新的接收事件目標(biāo)為空并且子視圖不為0
if (newTouchTarget == null && childrenCount != 0) {
//獲取x
final float x =
isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
//獲取y
final float y =
isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
//獲取預(yù)排續(xù)(按照Z(yǔ)order順序)子view集合
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
//是否滿足子view集合自定義排序
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
//子view數(shù)組
final View[] children = mChildren;
//根據(jù)zOrder由高到底遍歷預(yù)排續(xù)view數(shù)組
for (int i = childrenCount - 1; i >= 0; i--) {
//獲取子view的index
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
//獲取子view對(duì)象
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount;
}
//子視圖是否可以接收事件或者當(dāng)前事件的坐標(biāo)是否在子視圖中
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
//不滿足條件則開啟下一個(gè)View的遍歷循環(huán)
ev.setTargetAccessibilityFocus(false);
continue;
}
//如果找到了事件所在的子視圖
//getTouchTarget會(huì)根據(jù)子視圖獲取的newTouchTarget
//newTouchTarget不為空代表子視圖之前處理過(guò)事件(多指觸碰)
//對(duì)于單指down行為 newTouchTarget一定為null
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.
//更新接收事件目標(biāo)中觸控位的值
newTouchTarget.pointerIdBits |= idBitsToAssign;
//跳出循環(huán) 當(dāng)前已找到對(duì)應(yīng)處理該事件的view
break;
}
//如果有設(shè)置 PFLAG_CANCEL_NEXT_UP_EVENT,在此清除
resetCancelNextUpFlag(child);
//dispatchTransformedTouchEvent方法為事件找到真正處理它的子控件View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
//事件派發(fā)成功找到子控件View
//記錄最后一次down時(shí)間
mLastTouchDownTime = ev.getDownTime();
//存在預(yù)排序view集合
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
//遍歷預(yù)排續(xù)View數(shù)組獲取處理該事件的View的childIndex
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
//最后一次down事件index是原始子視圖的index
mLastTouchDownIndex = childIndex;
}
//獲取最后一次down事件的 x y坐標(biāo)
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//根據(jù)子視圖和觸控點(diǎn)創(chuàng)建newTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//flag設(shè)置為true
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
//清空預(yù)排序view集合
if (preorderedList != null) preorderedList.clear();
}
//如果沒(méi)找到事件所對(duì)應(yīng)的子view
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
//將最近一次處理事件的TouchTarget更新為處理該事件的TouchTarget
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
//如果沒(méi)有觸摸目標(biāo),則將觸摸事件視為普通的視圖
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//遍歷TouchTarget集合
while (target != null) {
//保存下一個(gè)TouchTarget到next
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
//如果已經(jīng)將事件派發(fā)到新的TouchTarget
handled = true;
} else {
//檢查View是否包含PFLAG_CANCEL_NEXT_UP_EVENT這個(gè)flag
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
//將事件派發(fā)到目標(biāo)視圖中,這里一般是處理Move事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
//處理成功
handled = true;
}
//如果需要去掉子視圖,也需要調(diào)整鏈表的中該視圖對(duì)于的touchTarget
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//處理up事件,清空touchTarget鏈表
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
//可拆分并且存在多指抬起
//根據(jù)actionIndex獲取觸控點(diǎn)idBitsToRemove然后調(diào)整touchTarget的觸控點(diǎn)
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
總結(jié):
1 關(guān)于ViewGroup的事件派發(fā),首先onFilterTouchEventForSecurity方法檢查接收事件的View是否被遮擋,遮擋的話是要cancel的,
2 對(duì)于一次down行為,可以理解為一次新的事件序列,要清空TouchTarget鏈表。
3 對(duì)于一次down行為,一定會(huì)被ViewGroup攔截進(jìn)行onInterceptTouchEvent方法檢查是否攔截成功,對(duì)于單指操作來(lái)說(shuō)一旦down事件被攔截 該次事件序列的MOVE UP都會(huì)被攔截但不在執(zhí)行onInterceptTouchEvent方法,子 View 可以通過(guò) requestDisallowInterceptTouchEvent 方法請(qǐng)求父 View 不要攔截
同樣關(guān)于onInterceptTouchEvent方法方法也不一定會(huì)攔截掉down事件,也要做條件判斷,后續(xù)細(xì)看
4 檢查事件是否被cancel
5 檢查事件資源是否為鼠標(biāo)事件
6 檢查事件是否可拆分
7 定義事件派發(fā)標(biāo)志位
8 在事件不被cancel和intercept時(shí),單指Down行為,多指可拆分的Pointer_Down行為,以及鼠標(biāo)懸浮MOVE行為,這種新事件序列行為
以單指Down行為來(lái)說(shuō)
首先獲取 x,y坐標(biāo)
獲取Zorder預(yù)排續(xù)的View集合
遍歷View集合 檢查View是否可以接收事件,事件坐標(biāo)是否在View內(nèi)
不滿足 輪詢下一個(gè)View
滿足 執(zhí)行dispatchTransformedTouchEvent方法 派發(fā)給子View處理該事件
處理成功,構(gòu)建newTouchTarget來(lái)保存View,后續(xù)事件序列的事件可以直接派發(fā)到該View
如果沒(méi)找到子View處理該事件,android的機(jī)制是讓最近一次的TouchTarget來(lái)處理該事件
接下來(lái)單指Move行為
就執(zhí)行下述mFirstTouchTarget鏈表遍歷,找到合適的子View處理行為事件
接下來(lái)單指Up行為
執(zhí)行resetTouchState()方法 清空鏈表
接下來(lái)看一下cancelAndClearTouchTargets方法
private void cancelAndClearTouchTargets(MotionEvent event) {
if (mFirstTouchTarget != null) {
boolean syntheticEvent = false;
if (event == null) {
final long now = SystemClock.uptimeMillis();
event = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
syntheticEvent = true;
}
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
resetCancelNextUpFlag(target.child);
dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
}
clearTouchTargets();
if (syntheticEvent) {
event.recycle();
}
}
}
1 檢查mFirstTouchTarget是否為空
2 如果事件為空 根據(jù)當(dāng)前時(shí)間生成Cancel事件,設(shè)置syntheticEvent為true
3 遍歷mFirstTouchTarget鏈表,執(zhí)行resetCancelNextUpFlag方法,向上次事件序列的子View發(fā)送cancel事件
4 清空touchTarget鏈表
接下來(lái)先看resetCancelNextUpFlag方法
private static boolean resetCancelNextUpFlag(@NonNull View view) {
if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
return true;
}
return false;
}
去掉視圖的 PFLAG_CANCEL_NEXT_UP_EVENT flag
接下來(lái)看dispatchTransformedTouchEvent方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
}
因?yàn)?上述中cancel為true會(huì)向View發(fā)送cancel事件取消上次事件序列
接下來(lái)看下clearTouchTargets方法
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
清空mFirstTouchTarget 觸控目標(biāo)列表 將觸控目標(biāo)的view設(shè)置為null。
接下來(lái)看
private void resetTouchState() {
//清空touchTarget
clearTouchTargets();
//如果View的PFLAG_CANCEL_NEXT_UP_EVENT存在則去除
resetCancelNextUpFlag(this);
//去除FLAG_DISALLOW_INTERCEPT flag
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
mNestedScrollAxes = SCROLL_AXIS_NONE;
}
接下來(lái)看下onInterceptTouchEvent方法
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
可以看到事件滿足下述條件才會(huì)返回true或者
或者actionMasked != MotionEvent.ACTION_DOWN才會(huì)設(shè)置 ,如果onInterceptTouchEvent失敗 intercepted = false。
接下來(lái)看下removePointersFromTouchTargets方法
private void removePointersFromTouchTargets(int pointerIdBits) {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if ((target.pointerIdBits & pointerIdBits) != 0) {
target.pointerIdBits &= ~pointerIdBits;
if (target.pointerIdBits == 0) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
這里遍歷TouchTarget鏈表的pointerIdBits與新事件的idBitsToAssign進(jìn)行比較,來(lái)清除TouchTarget鏈表的之前TouchTarget的pointerIdBits
接下來(lái)看下dispatchTransformedTouchEvent方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
//獲取事件Action
final int oldAction = event.getAction();
//如果存在cancel 則向View派發(fā)cancel事件
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
//獲取事件的觸控點(diǎn)集合
final int oldPointerIdBits = event.getPointerIdBits();
//根據(jù)事件觸控點(diǎn)集合和期待觸控點(diǎn)集合做運(yùn)算得到新的觸控點(diǎn)集合
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
//新的觸控點(diǎn)不存在的話派發(fā)失敗
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
//新觸控點(diǎn)和老觸控點(diǎn)相同
if (newPointerIdBits == oldPointerIdBits) {
//子視圖為空或者具有單位矩陣(即沒(méi)有進(jìn)行過(guò)縮放、旋轉(zhuǎn)或平移等變換)
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//子視圖為空 分發(fā)事件到viewGroup中
handled = super.dispatchTouchEvent(event);
} else {
//子視圖不為空并且由單位矩陣
//計(jì)算子視圖坐標(biāo)偏移量
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
//發(fā)送事件到子view
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {、
//根據(jù)newPointerIdBits拆分event
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
//由ViewGroup處理事件
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//坐標(biāo)轉(zhuǎn)換適配子view
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
//事件派發(fā)給子View
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
//回收transformedEvent
transformedEvent.recycle();
return handled;
}
總結(jié):
1檢查 even的Action是否為cancel,是的話直接發(fā)送cancel事件
2 根據(jù)event的觸控點(diǎn)集合和期待的觸控點(diǎn)集合進(jìn)行位運(yùn)算獲取新的觸控點(diǎn)集合
3 根據(jù)子視圖是否存在以及子視圖是否由單位矩陣
來(lái)決定事件的坐標(biāo)是否需要轉(zhuǎn)換成view坐標(biāo)系坐標(biāo),事件是由父View處理還是子View處理/子ViewGroup
4 派發(fā)完轉(zhuǎn)換事件,回收轉(zhuǎn)換事件
存在子View的情況,執(zhí)行child.dispatchTouchEvent方法
接下來(lái)看下View的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
//再確認(rèn)一次視圖是否被遮擋
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
//這里檢查mListenerInfo是否存在
//mOnTouchListener是否存在
//mViewFlags是ENABLED狀態(tài)
//上述條件滿足執(zhí)行l(wèi)i.mOnTouchListener.onTouch(this, event)
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//上述方式執(zhí)行失敗,執(zhí)行onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
事件處理順序mOnTouchListener.onTouch(this, event) -> onTouchEvent(event) -> onLongclick -> onClick
總結(jié) :
關(guān)于JAVA層事件的派發(fā)主要有三個(gè)方法
View/ViewGroup
dispatchTouchEvent 事件派發(fā)
onInterceptTouchEvent 事件攔截
onTouchEvent 事件處理
提出幾點(diǎn)疑問(wèn)
1 onclick 事件都是在 ACTION_UP 以后才被調(diào)用的
2 mOnTouchListener.onTouch(this, event) return true的話 是不會(huì)執(zhí)行onClick事件
3 子 View 可以通過(guò) requestDisallowInterceptTouchEvent 方法干預(yù)父 View 的事件分發(fā)過(guò)程 但是不包含down事件
接下來(lái)繼續(xù)分析源碼,同時(shí)也在分析源碼中解開上述問(wèn)題
上面View的dispatchTouchEvent方法會(huì)先檢查li.mOnTouchListener.onTouch(this, event)是否為true,true的話直接返回,不會(huì)在執(zhí)行onTouchEvent 中的onClick
如果是false的話執(zhí)行 View.onTouchEvent方法
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE
.......
//支持點(diǎn)擊
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
.....
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
//ACTION_UP執(zhí)行performClickInternal方法
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
case MotionEvent.ACTION_DOWN:
.......
else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(
ViewConfiguration.getLongPressTimeout(),
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
case MotionEvent.ACTION_MOVE:
final boolean deepPress =
motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
if (deepPress && hasPendingLongPressCallback()) {
// process the long click action immediately
removeLongPressCallback();
checkForLongClick(
0 /* send immediately */,
x,
y,
TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
}
break;
}
return true;
}
可以看到事件只要進(jìn)入到View的onTouchEvent方法,只要ViewFlag是支持CLICKABLE / LONG_CLICKABLE 事件就一定會(huì)被處理且返回true
在ACTION_UP的時(shí)候執(zhí)行performClickInternal函數(shù)
在ACTION_MOVE/ ACRION_DOWN 會(huì)執(zhí)行checkForLongClick
接下來(lái)看下performClickInternal函數(shù)
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
//執(zhí)行performClick函數(shù)
return performClick();
}
接下來(lái)看performClick函數(shù)
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
//ListenerInfo和li.mOnClickListener不為空
//播放click聲音
playSoundEffect(SoundEffectConstants.CLICK);
//執(zhí)行View的onclick函數(shù)
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
接下來(lái)看checkForLongClick函數(shù)
private void checkForLongClick(long delay, float x, float y, int classification) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
mPendingCheckForLongPress.rememberPressedState();
mPendingCheckForLongPress.setClassification(classification);
postDelayed(mPendingCheckForLongPress, delay);
}
}
構(gòu)建CheckForLongPress對(duì)象 延時(shí)post mPendingCheckForLongPress runnable函數(shù)
@Override
public void run() {
if ((mOriginalPressedState == isPressed()) && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
recordGestureClassification(mClassification);
if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
}
}
runnable的run方法 執(zhí)行performLongClick函數(shù)
最后會(huì)執(zhí)行到該函數(shù)
private boolean performLongClickInternal(float x, float y) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
可以看到這里執(zhí)行l(wèi)i.mOnLongClickListener.onLongClick(View.this)
這里的ListenerInfo.mOnLongClickListener是從哪里出現(xiàn)的
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
可以看到View的setOnClickListener這里實(shí)際就是我們應(yīng)用自己定義實(shí)現(xiàn)的OnClickListener對(duì)象,同樣onClick / onLongClick也會(huì)回調(diào)到我們自己重寫的方法中