Android溢出菜单子菜单项添加OnLongClickListener失效问题求助
Hey there! I totally get how frustrating it is when a feature you’ve been working on just won’t fire—let’s break down why your long click listeners aren’t working for those submenu items, and fix it.
Why Your Current Code Isn’t Working
The core issue here is that when you set app:actionViewClass on your submenu items, the Android system doesn’t actually use that view when rendering the overflow submenu. For pop-up style submenus (like the one triggered by your "Help" item), Android uses its own default layout and views to display the options. That means the actionView you’re trying to bind the long click to never actually gets shown—so your listener is attached to a view that’s never on screen.
Solution 1: Use a Custom PopupMenu (Recommended for Compatibility)
The most reliable way to add long click support is to replace the system’s built-in submenu with a custom PopupMenu. This lets you fully control the menu’s behavior and easily attach both click and long click listeners.
Step 1: Move Submenu to Separate XML
Create a new menu file res/menu/submenu_help.xml with your submenu items:
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/event1" android:title="Event 1" /> <item android:id="@+id/event2" android:title="Event 2" /> <item android:id="@+id/event3" android:title="Event 3" /> </menu>
Step 2: Handle Parent Menu Click & Attach Listeners
In your Activity’s onOptionsItemSelected method, trigger the custom PopupMenu when the "Help" item is clicked, then attach both click and long click listeners:
override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.help -> { // Create PopupMenu anchored to the help menu item val popupMenu = PopupMenu(this, findViewById(R.id.help)) popupMenu.menuInflater.inflate(R.menu.submenu_help, popupMenu.menu) // Attach long click listener once the menu is shown popupMenu.setOnShowListener { // Access the underlying ListView of the PopupMenu val popupField = PopupMenu::class.java.getDeclaredField("mPopup") popupField.isAccessible = true val menuPopupHelper = popupField.get(popupMenu) val getListViewMethod = menuPopupHelper.javaClass.getDeclaredMethod("getListView") getListViewMethod.isAccessible = true val listView = getListViewMethod.invoke(menuPopupHelper) as ListView // Set long click listener listView.onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, position, _ -> val selectedItem = popupMenu.menu.getItem(position) when (selectedItem.itemId) { R.id.event1 -> Log.d("Long Click", "Event 1 was long pressed!") R.id.event2 -> Log.d("Long Click", "Event 2 was long pressed!") R.id.event3 -> Log.d("Long Click", "Event 3 was long pressed!") } true } } // Handle regular click events popupMenu.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { R.id.event1 -> { Log.d("Click", "Event 1 clicked") true } R.id.event2 -> { Log.d("Click", "Event 2 clicked") true } R.id.event3 -> { Log.d("Click", "Event 3 clicked") true } else -> false } } popupMenu.show() true } else -> super.onOptionsItemSelected(item) } }
Solution 2: Target the System’s Submenu ListView (Less Reliable)
If you want to stick with the system’s built-in submenu, you can find the underlying ListView that renders the submenu items and attach a long click listener to it. Note that this relies on internal Android views, so it might break across different versions or OEM skins.
override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.help -> { // Wait a short time for the submenu to render Handler(Looper.getMainLooper()).postDelayed({ findOverflowSubmenuListView()?.let { listView -> listView.onItemLongClickListener = AdapterView.OnItemLongClickListener { parent, _, position, _ -> val subMenuItem = parent.adapter.getItem(position) as MenuItem when (subMenuItem.itemId) { R.id.event1 -> Log.d("Long Click", "Event 1 long pressed") R.id.event2 -> Log.d("Long Click", "Event 2 long pressed") R.id.event3 -> Log.d("Long Click", "Event 3 long pressed") } true } } }, 100) true } else -> super.onOptionsItemSelected(item) } } // Helper to find the submenu's ListView by traversing the decor view private fun findOverflowSubmenuListView(): ListView? { val decorView = window.decorView as ViewGroup val popupViews = mutableListOf<View>() traverseViewsForPopups(decorView, popupViews) return popupViews.firstOrNull { it is ListView && (it as ListView).adapter.count == 3 } as? ListView } private fun traverseViewsForPopups(parent: ViewGroup, popupViews: MutableList<View>) { for (i in 0 until parent.childCount) { val child = parent.getChildAt(i) val className = child.javaClass.simpleName if (className.contains("PopupWindow") || className.contains("MenuPopup")) { if (child is ViewGroup) { traverseViewsForPopups(child, popupViews) child.children.forEach { subChild -> if (subChild is ListView) popupViews.add(subChild) } } } else if (child is ViewGroup) { traverseViewsForPopups(child, popupViews) } } }
Final Notes
- Solution 1 (Custom PopupMenu) is the better choice for long-term compatibility and flexibility—it doesn’t depend on Android’s internal view structures, which can change between versions.
- Make sure to remove the
app:actionViewClassattributes from your original menu XML since they’re not being used anymore.
内容的提问来源于stack exchange,提问作者Gino

