AppCompatActivity#closeOptionsMenu适配API30后无法关闭菜单问题求助
修复方案
方案1:从根源拦截系统菜单弹出(推荐,兼容性最好)
如果你不需要使用系统默认的选项菜单,完全替换为自定义bottom sheet,可以直接拦截菜单创建和触发逻辑,避免后续关闭的问题:
- 重写
onCreateOptionsMenu直接返回false,告知系统无需创建默认选项菜单:
@Override public boolean onCreateOptionsMenu(Menu menu) { // 不创建系统菜单 return false; }
- 分别拦截两类菜单触发入口:
- 实体菜单键触发:重写
onKeyDown方法消费菜单键事件,直接弹出你的bottom sheet
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) { showYourBottomSheet(); // 消费事件,阻止系统继续处理 return true; } return super.onKeyDown(keyCode, event); }
- ActionBar/Toolbar上的三点溢出按钮触发:替换溢出按钮的点击逻辑
// 如果你使用Toolbar作为ActionBar,在初始化Toolbar时添加以下逻辑 toolbar.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { toolbar.getViewTreeObserver().removeOnPreDrawListener(this); // 遍历找到溢出菜单按钮 for (int i = 0; i < toolbar.getChildCount(); i++) { View child = toolbar.getChildAt(i); if (child instanceof ActionMenuView) { ActionMenuView menuView = (ActionMenuView) child; for (int j = 0; j < menuView.getChildCount(); j++) { View innerChild = menuView.getChildAt(j); String overflowDesc = getString(androidx.appcompat.R.string.abc_action_menu_overflow_description); if (overflowDesc.equals(innerChild.getContentDescription())) { innerChild.setOnClickListener(v -> { showYourBottomSheet(); }); break; } } break; } } return true; } });
方案2:需要保留系统菜单逻辑,仅特定场景关闭
如果你的场景需要保留部分系统菜单逻辑,只需要在特定时机关闭已经弹出的菜单,需要调整closePanel的调用时机,避免和系统的菜单显示逻辑冲突:
// 将关闭逻辑post到主线程消息队列末尾,等系统的菜单显示逻辑执行完成后再关闭 new Handler(Looper.getMainLooper()).post(() -> { getWindow().closePanel(Window.FEATURE_OPTIONS_PANEL); // 关闭完成后再弹出你的bottom sheet showYourBottomSheet(); });
之前直接调用closePanel不生效的核心原因是调用时机在系统的菜单显示逻辑之前,系统后续的显示任务还是会把菜单展示出来,post到消息队列末尾就可以保证先执行系统的显示逻辑,再执行关闭逻辑,最终达到隐藏菜单的效果。
内容的提问来源于stack exchange,提问作者Noa Drach
相关产品推荐
相关产品推荐

