#android #android-layout #android-dialogfragment #popupmenu
Вопрос:
Так что эта проблема уже давно сводит меня с ума, и я нигде не мог найти решения. В основном у меня есть фрагмент диалогового окна с двумя плавающими кнопками действий вверху. При нажатии правой кнопки мыши создается это всплывающее меню. Пока все хорошо. Но, как вы можете видеть на картинке, всплывающее меню нарушает режим погружения, из-за чего появляется панель навигации, и по какой-то причине последний элемент меню не обрезается и рисуется под ним. Очевидно, что это проблема, потому что, если вы попытаетесь щелкнуть по ней, вместо этого вы будете взаимодействовать с панелью навигации.
Вот код для моей кнопки и всплывающего меню:
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, а затем прокрутить, чтобы увидеть другие, это тоже было бы исправлением, но я снова не смог найти способ сделать это.
Любые предложения будут оценены по достоинству!
Ответ №1:
Отказ от ответственности
Это не прямое решение проблемы, но вы можете использовать его в качестве последнего средства.
Если вы заметили, что ваше всплывающее меню опускается в нижнюю часть экрана, и причина в том, что представление привязки находится в верхней половине экрана.
Если вы изменили привязку на какой-то другой вид внизу (возможно, вам нужно создать какой-то невидимый вид), то всплывающее меню откроется в верхней части экрана; и в этом случае оно никогда не будет пересекаться с нижней панелью навигации.
Ответ №2:
- Всплывающее меню прерывает режим погружения, из-за чего появляется панель навигации.
Я думаю, что это предполагаемая спецификация пользовательского интерфейса Android. Когда появляется меню, ваше окно теряет фокус и снова появляется скрытая панель навигации.
- По какой-то причине последний элемент меню не обрезается и рисуется под ним. Очевидно, что это проблема, потому что, если вы попытаетесь щелкнуть по ней, вместо этого вы будете взаимодействовать с панелью навигации.
Панель навигации расположена над всплывающим окном. Опять же, я думаю, что это намеренно. Например, вы можете вернуться в предыдущее состояние (закрыть всплывающее окно и т. Д.).
Вы можете вручную прокрутить всплывающее меню и избежать такого перекрывающегося состояния.
Комментарии:
1. Для 1. Я согласен с тем, что он предназначен для 2. Я не уверен, потому что код, который я написал в 1. делает так, чтобы последний показанный элемент был на 12 часов раньше , и чтобы увидеть последний, вам придется прокрутить (в основном он прикрепляет меню к панели навигации), что меня устраивает. Проблема в том, что этот код, похоже, не работает на большинстве устройств, и я не знаю, как добиться одинакового поведения на всех устройствах.
2. Также не могли бы вы, пожалуйста, добавить объяснение вашего последнего предложения?
3. @VeselinZinkov Я имею в виду, что прокрутка — это ручное действие пользователя, а не программное поведение.
Ответ №3:
Кажется, я наконец-то понял, что происходит. Есть несколько вещей, которые необходимо переработать. Поэтому для всех, у кого есть эта проблема, попробуйте это:
1. У меня есть один способ скрыть пользовательский интерфейс системы и один для его отображения.
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: Оказывается, что представление флага.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());
});