Android — Пункты всплывающего меню отображаются под панелью навигации

#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:

  1. Всплывающее меню прерывает режим погружения, из-за чего появляется панель навигации.

Я думаю, что это предполагаемая спецификация пользовательского интерфейса Android. Когда появляется меню, ваше окно теряет фокус и снова появляется скрытая панель навигации.

  1. По какой-то причине последний элемент меню не обрезается и рисуется под ним. Очевидно, что это проблема, потому что, если вы попытаетесь щелкнуть по ней, вместо этого вы будете взаимодействовать с панелью навигации.

Панель навигации расположена над всплывающим окном. Опять же, я думаю, что это намеренно. Например, вы можете вернуться в предыдущее состояние (закрыть всплывающее окно и т. Д.).

Вы можете вручную прокрутить всплывающее меню и избежать такого перекрывающегося состояния.

скриншот

Комментарии:

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());
        });