Как полноэкранные DrawerLayouts влияют на добавление фрагментов в backstack при установке окна SystemUiVisibility в SYSTEM_UI_FLAG_LIGHT_STATUS_BAR? - PullRequest
0 голосов
/ 16 января 2019

Я столкнулся с проблемой, связанной с фрагментами, в более крупном проекте, над которым я работаю, и я создал минимальный проверяемый пример, чтобы объяснить его:

Пример настройки:

Пример проекта - это приложение без панели действий (тема имеет Theme.AppCompat.Light.NoActionBar в качестве родителя), с targetSdkVersion 27 и minSdkVersion 23.

Основным действием является AppCompatActivity с android.support.v4.widget.DrawerLayout в качестве корневого макета. Этот DrawerLayout имеет android:fitsSystemWindows="true", чтобы перекрывать строку состояния.

Внутри DrawerLayout есть FrameLayout, в котором будет android.support.v4.app.Fragment. Фрагмент добавляется в интерфейс и в backstack при нажатии кнопки:

FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragmentContent, fragment);
transaction.addToBackStack(fragment.getClass().getName());
transaction.commitAllowingStateLoss();

Этот фрагмент показывает кнопку, которая удаляет фрагмент, вызывая onBackPressed для действия.

Пока что поведение приложения нормальное: фрагмент добавляется при нажатии кнопки на макете активности, а затем удаляется при нажатии кнопки на макете фрагмента.

Проблема:

Я добавляю в действие новую кнопку, которая при касании устанавливает цвет строки состояния на белый, а SystemUiVisibility для вида оформления окна - на SYSTEM_UI_FLAG_LIGHT_STATUS_BAR:

Window window = getWindow();
window.setStatusBarColor(ContextCompat.getColor(this, android.R.color.white));
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

После нажатия этой кнопки строка состояния, как и ожидалось, отображается черным текстом на белом фоне.

Неожиданная часть заключается в том, что после этого изменения фрагмент больше не отображается в интерфейсе, когда я нажимаю кнопку, которая добавляет его .

Фрагмент по-прежнему помещается в backstack, поскольку нажатие клавиши возврата системы не сразу завершает действие, а удаляет фрагмент. Я подтвердил это, добавив операторы log в методы фрагмента onResume и onPause и убедившись, что они вызываются, когда фрагмент добавляется и удаляется из backstack.

Расследование:

Я пробовал несколько обходных путей и исследовал несколько возможных причин.

Что мне удалось найти:

  • Проблема возникает только для некоторых конфигураций приложения. Все это должно быть верно, чтобы проблема проявилась:

    1. Тема приложения имеет Theme.AppCompat.Light.NoActionBar в качестве родителя.
    2. Мероприятие имеет в качестве корневого макета android.support.v4.widget.DrawerLayout.
    3. DrawerLayout имеет android:fitsSystemWindows="true".
    4. Должны быть установлены как цвет строки состояния, так и SystemUiVisibility для вида оформления окна.
    5. SystemUiVisibility для вида декорации окна должно быть установлено на SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.

    & EnSP;

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

& EnSP;

  • Я рассмотрел код для DrawerLayout, как возможный источник понимания этого вопроса. Я даже создал локальную копию компонента, чтобы можно было закомментировать различные разделы и добавить операторы журнала для отслеживания поведения.

    Мне удалось отследить проблему до DrawerLayout, установив для SystemUiVisibility значение SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.

    Когда эти флаги установлены, OnApplyWindowInsetsListener вызывается, когда я устанавливаю белый цвет строки состояния, а SystemUiVisibility для вида декорации окна - SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.

    В свою очередь, этот слушатель вызывает метод setChildInsets, который в конце вызывает requestLayout() для DrawerLayout.

    Этот запрос макета является последним, полученным DrawerLayout. С этого момента его метод onLayout больше не вызывается при установке цвета строки состояния и SystemUiVisibility для представления оформления окна, а также при добавлении фрагмента. Ни метод onMeasure, ни OnApplyWindowInsetsListener никогда не будут вызваны после этой точки. Представления фрагментов больше не отображаются, и их размер показывает, что он остается равным 0.

код:

Вы можете скачать пример проекта .

Вопрос (ы):

Что вызывает это, как и почему? Имеет ли он какое-либо отношение к DrawerLayout напрямую или его поведение является лишь следствием, вызванным чем-то другим?

Почему это происходит только в том случае, если в приложении нет панели действий и когда SystemUiVisibility для представления оформления окна установлено только на SYSTEM_UI_FLAG_LIGHT_STATUS_BAR?

1 Ответ

0 голосов
/ 24 января 2019

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

Также возможно, что причина, по которой это вызывает ошибки, заключается в том, что вы не применяете / удаляете другие флаги, упомянутые в документация setStatusBarColor().Попробуйте это:

// clear FLAG_TRANSLUCENT_STATUS flag:
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

// finally change the color
window.setStatusBarColor(ContextCompat.getColor(activity,R.color.my_statusbar_color));

Как ни странно, я был в состоянии воспроизвести проблему, описанную вами после попытки кода выше, что делает его еще более запутанным.

Поэтому я рекомендую альтернативный подход - использовать CoordinatorLayout вместо окна для рисования цвета строки состояния.Сделайте CoordinatorLayout прямым потомком DrawerLayout и добавьте к нему android:fitsSystemWindows="true".Тогда вместо того, чтобы звонить window.setStatusBarColor(), звоните coordinatorLayout.setStatusBarBackgroundColor().Это решение не требует каких-либо специальных оконных флагов, но учтите, что для того, чтобы это работало, вам также необходимо добавить следующую строку в тему:

<item name="android:statusBarColor">@android:color/transparent</item>

Я также использую CoordinatorLayout в большом проекте для динамическогоизменив цвет строки состояния, так что, надеюсь, она подойдет и вам.Это определенно работает в примере проекта.

...