點擊 Button1 彈出 Button2/3/4 菜單,點擊菜單**外部任意區域**自動隱藏菜單。
// 在 Activity/Fragment 中
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (ev.action == MotionEvent.ACTION_DOWN && isMenuShowing) {
val menuRect = Rect()
menuContainer.getGlobalVisibleRect(menuRect)
// 點擊座標不在菜單區域內
if (!menuRect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
hideMenu()
return true // 消費事件,不再分發給子 View
}
}
return super.dispatchTouchEvent(ev)
}
rootLayout.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN && isMenuShowing) {
val location = IntArray(2)
menuContainer.getLocationOnScreen(location)
val menuRect = Rect(location[0], location[1],
location[0] + menuContainer.width,
location[1] + menuContainer.height)
if (!menuRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
hideMenu()
return@setOnTouchListener true
}
}
false // 不攔截,繼續分發
}
val popup = PopupWindow(menuView, WRAP_CONTENT, WRAP_CONTENT).apply {
isOutsideTouchable = true // 關鍵屬性
backgroundDrawable = ColorDrawable(Color.TRANSPARENT) // 必須設置背景才生效
setOnDismissListener { isMenuShowing = false }
}
popup.showAsDropDown(button1)
✅ 幾行代碼搞定、系統級動畫、自動處理鍵盤/旋轉
✅ 推薦作為首選方案
menuView.setOnTouchListener { _, _ -> true } // 消費所有觸摸事件,不向外分發
| 問題 | 原因 | 解決 |
|---|---|---|
| 外部點擊不隱藏 | PopupWindow 未設置 backgroundDrawable | 設置透明 ColorDrawable |
| 菜單項點擊無響應 | 外部攔截器消費了 DOWN 事件 | 只在 ACTION_DOWN 判斷座標,返回 false 繼續分發 |
| 彈出鍵盤時菜單不消失 | 輸入法窗口不在 Activity 視圖樹內 | 監聽鍵盤彈出事件手動 hideMenu() |
Android 點擊外部隱藏菜單,**首選 `PopupWindow.setOutsideTouchable(true)`** 系統級方案;自定義 View 時在 `dispatchTouchEvent` 或根容器 `OnTouchListener` 中通過座標判斷是否在菜單矩形外。核心邏輯:**獲取菜單全局座標矩形 → 對比觸摸點原始座標 → 外部則隱藏並消費事件**。