首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Android View 事件分發機制:onTouchEvent 執行於 dispatchTouchEvent 之前的原因分析

     編輯:程式設計疑難解答

現象描述

自定義 MyButton 重寫 dispatchTouchEvent 和 onTouchEvent,並設置 OnTouchListener。日誌顯示 onTouchEvent 先於 dispatchTouchEvent 輸出,似乎違背「分發在前、處理在後」的認知。

代碼關鍵點

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    // 日誌打印
    return super.dispatchTouchEvent(event); // 調用父類實現
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    // 日誌打印
    return super.onTouchEvent(event);
}

// Activity 中
button.setOnTouchListener((v, event) -> false); // 返回 false

事件分發標準流程

View.dispatchTouchEvent(event)
    │
    ├─ 1. OnTouchListener.onTouch()  → 返回 true 則消費事件,流程結束
    │
    ├─ 2. onTouchEvent(event)        → 返回 true 則消費事件
    │     ├─ 手勢檢測、長按等
    │     └─ performClick() → OnClickListener.onClick()
    │
    └─ 返回結果(true/false)

為何日誌顯示 onTouchEvent 先執行?

關鍵:super.dispatchTouchEvent() 內部調用了 onTouchEvent()

查看 Android 源碼 View.dispatchTouchEvent 簡化邏輯:

public boolean dispatchTouchEvent(MotionEvent event) {
    // ... 
    if (onTouchListener != null && onTouchListener.onTouch(this, event)) {
        return true;
    }
    return onTouchEvent(event); // 直接調用 onTouchEvent!
}

因此:
1. 進入 MyButton.dispatchTouchEvent(子類重寫)
2. 打印「dispatchTouchEvent: DOWN」日誌
3. 調用 super.dispatchTouchEvent(event)(父類 View 實現)
4. 父類內部調用 onTouchEvent(event)
5. 進入 MyButton.onTouchEvent(子類重寫)
6. 打印「onTouchEvent: DOWN」日誌
7. 返回層層回溯
8. 回到子類 dispatchTouchEvent,打印「==========================」分隔符
9. 返回結果

日誌序列解讀

// 按下時
MyButton|onTouchEvent|event:DOWN       // 步驟 5-6:父類 dispatch 內部調用 onTouchEvent
MyButton|dispatchTouchEvent|event:DOWN // 步驟 2-3:子類 dispatch 打印,然後調用 super
==========================             // 步驟 8:子類 dispatch 打印分隔符
MyButton|onTouchEvent|event:DOWN       // 步驟?:可能是 performClick 內部再次調用?或日誌重複

第三行 onTouchEvent 可能來自 performClick() 觸發的再次分發,或 OnTouchListener 返回 false 導致的複雜流程。

核心結論

  • dispatchTouchEvent 是入口,onTouchEvent 是其內部調用的環節之一,非並行獨立方法。
  • 子類重寫 dispatchTouchEvent 時,日誌打印位置決定觀察順序:若在 super.dispatchTouchEvent() 前打印,則顯示「dispatch 先」;若在後打印,則顯示「onTouchEvent 先」。
  • 真正的執行順序未變:分發 → 監聽器 → 處理(onTouchEvent)。

最佳實踐建議

  • 日誌打印放在 super 調用前後對比:
    Log.d("BEFORE super"); super.dispatchTouchEvent(event); Log.d("AFTER super");
  • 理解返回值含義:true = 消費事件,阻止向上傳遞;false = 向父 View 傳遞。
  • 區分 View 與 Activity 分發:Activity 無 onInterceptTouchEvent,直接 dispatchTouchEvent → onTouchEvent。

總結

日誌顯示 onTouchEvent 先於 dispatchTouchEvent 打印,是因為子類 dispatchTouchEvent 中的日誌語句位於 super.dispatchTouchEvent() 調用之後,而父類 dispatchTouchEvent 內部同步調用了 onTouchEvent。這不代表事件分發機制改變,僅是代碼執行順序與日誌打印位置造成的視覺誤導。理解 View.dispatchTouchEvent 源碼邏輯,即可釐清真實調用鏈。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved