Android - 弹出菜单项显示在导航栏下方
Android - Popup menu items appear under navigation bar
所以这个问题已经让我发疯了很长一段时间,我找不到任何解决方案。基本上我有一个对话框片段,顶部有两个浮动操作按钮。单击右边的那个,创建这个弹出菜单。到目前为止,一切都很好。但是正如您从图片中看到的那样,弹出菜单打破了导致导航栏出现的沉浸式模式,并且由于某种原因,菜单中的最后一个元素没有被剪裁并绘制在它下面。这显然是一个问题,因为如果您尝试单击它,您最终会与导航栏进行交互。
这是我的按钮和弹出菜单的代码:
notificationSchedule.setOnClickListener(v -> {
PopupMenu notificationMenu = new PopupMenu(requireActivity(), v);
notificationMenu.inflate(R.menu.notification_changer);
SpannableString spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).setTitle(spannableString1);
if (notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu() != null) {
spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).setTitle(spannableString1);
}
notificationMenu.show();
notificationMenu.setOnMenuItemClickListener(item -> {
boolean value = managePopupMenuClick(-1, item);
if (notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu() != null) {
SpannableString spannableString2 = new SpannableString(notificationMenu.getMenu().findItem(item.getItemId()).getTitle() + " +");
spannableString2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString2.length(), 0);
notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu().setHeaderTitle(spannableString2);
}
return value;
});
});
到目前为止我已经尝试过:
1. 我最接近解决这个问题的是我的 onCreateView() 方法中的代码和平:
dialogWindow.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
if (visibility == 4) dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
});
根据我的测试,这解决了问题,但在极少数设备上。
2. 我尝试在显示弹出菜单后立即隐藏导航栏,但效果不佳。
3.我搜索了一种在显示弹出菜单的同时保持身临其境模式的方法,但找不到方法。
4. 我还搜索了一种方法来尝试限制弹出菜单中可见项目的数量。例如,如您所见,此弹出菜单中有 11 个项目。例如,如果有一种方法可以将可见项的数量限制为 4,然后滚动查看其他项也可以修复,但我再次找不到方法。
如有任何建议,我们将不胜感激!
- 弹出菜单打破了导致导航栏出现的沉浸式模式。
我认为这是 Android UI 的预期规范。弹出菜单时,您的 window 失去焦点,隐藏的导航栏重新出现。
- 由于某种原因,菜单中的最后一个元素没有被剪裁,而是绘制在其下方。这显然是一个问题,因为如果您尝试单击它,您最终会与导航栏进行交互。
导航栏位于弹出窗口上方。我再次认为这是有意的。例如,您可以导航回之前的状态(关闭弹出窗口等)。
您可以手动向上滚动弹出菜单,避免这种重叠状态。
免责声明
This is not a direct solution to the problem, but you can use it as a last resort.
如果您注意到弹出菜单向屏幕底部下拉,原因是锚视图位于屏幕的上半部分。
如果您将锚点更改为底部的其他视图(可能需要创建一些不可见视图),则弹出菜单将向屏幕顶部打开;在这种情况下,它永远不会与底部导航栏相交。
我想我终于知道发生了什么事了。有几件事需要返工。因此,对于遇到此问题的任何其他人,请尝试以下操作:
1.我有一种隐藏系统的方法UI和一种显示它的方法。
private void hideSystemUI() {
dialogWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
private void showSystemUI() {
dialogWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
注意 1:原来是标志 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 导致了这个问题,您可以从标志描述中读到.
2. 我最终从 onCreateView():
中删除了这段代码
dialogWindow.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
if (visibility == 4) dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
});
注意 2: 但是再次证明,需要清除这些标志。所以这段代码dialogWindow.clearFlags(...);就不得不用了
所以我的 onClickListener() 的最终版本看起来像这样(在每个注释块下面都有代码更改):
notificationSchedule.setOnClickListener(v -> {
// Show the system UI before creating the popup menu
// but without the View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION flag !!!
dialogWindow.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
// Instead of having a setOnSystemUiVisibilityChangeListener() in the onCreateView()
// clear the flags here !!!
dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
// I also ended up using another constructor for the popup menu because I wanted
// to use the R.attr.actionOverflowMenuStyle
// This is not needed, but if you do want to use it know that it requires API 22
PopupMenu notificationMenu;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
notificationMenu = new PopupMenu(requireActivity(), v, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0);
} else {
notificationMenu = new PopupMenu(requireActivity(), v);
}
notificationMenu.inflate(R.menu.notification_changer);
SpannableString spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).setTitle(spannableString1);
if (notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu() != null) {
spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).setTitle(spannableString1);
}
notificationMenu.show();
notificationMenu.setOnMenuItemClickListener(item -> {
boolean value = managePopupMenuClick(-1, item);
if (notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu() != null) {
SpannableString spannableString2 = new SpannableString(notificationMenu.getMenu().findItem(item.getItemId()).getTitle() + " +");
spannableString2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString2.length(), 0);
notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu().setHeaderTitle(spannableString2);
}
return value;
});
// Finally add a onDismissListener() and hide the system UI inside it
notificationMenu.setOnDismissListener(menu -> hideSystemUI());
});
所以这个问题已经让我发疯了很长一段时间,我找不到任何解决方案。基本上我有一个对话框片段,顶部有两个浮动操作按钮。单击右边的那个,创建这个弹出菜单。到目前为止,一切都很好。但是正如您从图片中看到的那样,弹出菜单打破了导致导航栏出现的沉浸式模式,并且由于某种原因,菜单中的最后一个元素没有被剪裁并绘制在它下面。这显然是一个问题,因为如果您尝试单击它,您最终会与导航栏进行交互。
这是我的按钮和弹出菜单的代码:
notificationSchedule.setOnClickListener(v -> {
PopupMenu notificationMenu = new PopupMenu(requireActivity(), v);
notificationMenu.inflate(R.menu.notification_changer);
SpannableString spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).setTitle(spannableString1);
if (notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu() != null) {
spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).setTitle(spannableString1);
}
notificationMenu.show();
notificationMenu.setOnMenuItemClickListener(item -> {
boolean value = managePopupMenuClick(-1, item);
if (notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu() != null) {
SpannableString spannableString2 = new SpannableString(notificationMenu.getMenu().findItem(item.getItemId()).getTitle() + " +");
spannableString2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString2.length(), 0);
notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu().setHeaderTitle(spannableString2);
}
return value;
});
});
到目前为止我已经尝试过:
1. 我最接近解决这个问题的是我的 onCreateView() 方法中的代码和平:
dialogWindow.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
if (visibility == 4) dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
});
根据我的测试,这解决了问题,但在极少数设备上。
2. 我尝试在显示弹出菜单后立即隐藏导航栏,但效果不佳。
3.我搜索了一种在显示弹出菜单的同时保持身临其境模式的方法,但找不到方法。
4. 我还搜索了一种方法来尝试限制弹出菜单中可见项目的数量。例如,如您所见,此弹出菜单中有 11 个项目。例如,如果有一种方法可以将可见项的数量限制为 4,然后滚动查看其他项也可以修复,但我再次找不到方法。
如有任何建议,我们将不胜感激!
- 弹出菜单打破了导致导航栏出现的沉浸式模式。
我认为这是 Android UI 的预期规范。弹出菜单时,您的 window 失去焦点,隐藏的导航栏重新出现。
- 由于某种原因,菜单中的最后一个元素没有被剪裁,而是绘制在其下方。这显然是一个问题,因为如果您尝试单击它,您最终会与导航栏进行交互。
导航栏位于弹出窗口上方。我再次认为这是有意的。例如,您可以导航回之前的状态(关闭弹出窗口等)。
您可以手动向上滚动弹出菜单,避免这种重叠状态。
免责声明
This is not a direct solution to the problem, but you can use it as a last resort.
如果您注意到弹出菜单向屏幕底部下拉,原因是锚视图位于屏幕的上半部分。
如果您将锚点更改为底部的其他视图(可能需要创建一些不可见视图),则弹出菜单将向屏幕顶部打开;在这种情况下,它永远不会与底部导航栏相交。
我想我终于知道发生了什么事了。有几件事需要返工。因此,对于遇到此问题的任何其他人,请尝试以下操作:
1.我有一种隐藏系统的方法UI和一种显示它的方法。
private void hideSystemUI() {
dialogWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
private void showSystemUI() {
dialogWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
注意 1:原来是标志 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 导致了这个问题,您可以从标志描述中读到.
2. 我最终从 onCreateView():
中删除了这段代码dialogWindow.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
if (visibility == 4) dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
});
注意 2: 但是再次证明,需要清除这些标志。所以这段代码dialogWindow.clearFlags(...);就不得不用了
所以我的 onClickListener() 的最终版本看起来像这样(在每个注释块下面都有代码更改):
notificationSchedule.setOnClickListener(v -> {
// Show the system UI before creating the popup menu
// but without the View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION flag !!!
dialogWindow.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
// Instead of having a setOnSystemUiVisibilityChangeListener() in the onCreateView()
// clear the flags here !!!
dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
// I also ended up using another constructor for the popup menu because I wanted
// to use the R.attr.actionOverflowMenuStyle
// This is not needed, but if you do want to use it know that it requires API 22
PopupMenu notificationMenu;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
notificationMenu = new PopupMenu(requireActivity(), v, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0);
} else {
notificationMenu = new PopupMenu(requireActivity(), v);
}
notificationMenu.inflate(R.menu.notification_changer);
SpannableString spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).setTitle(spannableString1);
if (notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu() != null) {
spannableString1 = new SpannableString(notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).getTitle());
spannableString1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString1.length(), 0);
notificationMenu.getMenu().getItem(databaseNotification.getIndex1()).getSubMenu().getItem(databaseNotification.getIndex2()).setTitle(spannableString1);
}
notificationMenu.show();
notificationMenu.setOnMenuItemClickListener(item -> {
boolean value = managePopupMenuClick(-1, item);
if (notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu() != null) {
SpannableString spannableString2 = new SpannableString(notificationMenu.getMenu().findItem(item.getItemId()).getTitle() + " +");
spannableString2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary)), 0, spannableString2.length(), 0);
notificationMenu.getMenu().findItem(item.getItemId()).getSubMenu().setHeaderTitle(spannableString2);
}
return value;
});
// Finally add a onDismissListener() and hide the system UI inside it
notificationMenu.setOnDismissListener(menu -> hideSystemUI());
});