Обновите Backstack Activities после изменения ночного режима

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

Скажем, у меня есть бэкстэк A> B> C. Действие C позволяет изменить ночной режим, позвонив AppCompatDelegate.setDefaultNightMode(). После этого вызова текущее действие (C) может обновить свою тему с помощью delegate.applyDayNight() или recreate().

Однако, когда пользователь возвращается к B или A, действия по-прежнему используют «старый» режим, дневной или ночной.

Я попытался добавить в Activity что-то подобное:

override fun onResume() {
  super.onResume()
  delegate.applyDayNight()
}

Но вроде не работает.

Я сделал несколько попыток исправить это:

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

Другая идея - иметь класс, который обрабатывает изменение ночного режима и предоставляет LiveData. Каждое действие будет прослушивать LiveData для изменения режима и вызывать recreate(). Однако мы застряли в бесконечном цикле, потому что Activity будет воссоздавать сразу после начала прослушивания LiveData.

Мне трудно поверить, что я первый, кто пытается обновить Activity из backstack после смены ночного режима. Что я пропустил?

Спасибо!


person Jonas Schmid    schedule 28.02.2019    source источник
comment
Вы можете попробовать создать подкласс AppCompatDelegate   -  person Shamsul Arefin Sajib    schedule 05.03.2019
comment
всякий раз, когда вы меняете настройки приложения, вы должны воссоздавать все действия, потому что, как и при изменении языка в C, действия A и B остаются на предыдущем языке. Так что лучше перезапустить приложение.   -  person Tanveer Munir    schedule 07.03.2019
comment
Нет, я хочу сохранить свой бэкстэк.   -  person Jonas Schmid    schedule 07.03.2019


Ответы (4)


Если вы можете обнаружить изменение дневного / ночного режима, вы можете просто воссоздать действие, которое возобновляется при извлечении заднего стека.

В следующей демонстрации есть три действия: A, B и C. A создает B, а B создает C. Действие C может изменять дневной / ночной режим. Когда C появляется, действие B видит изменение дневного / ночного режима и вызывает reCreate(), чтобы воссоздать действие. То же самое происходит в действии A, когда действие B появляется.

На видео ниже показан эффект. Светлый фон - это «дневной» режим, а темный - «ночной».

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

введите описание изображения здесь

person Cheticamp    schedule 11.03.2019
comment
Да, я тоже к этому прибегал. Не похоже, что у кого-то есть лучшее решение. Спасибо ! - person Jonas Schmid; 11.03.2019
comment
@Jonas Мне любопытно, что вас не устраивает в этом решении. - person Cheticamp; 11.03.2019
comment
Это тот код, который попадает в BaseActivity. И я делаю все возможное, чтобы не допустить появления тех классов монстров, которые много делают. Думаю, жизнь Android-разработчика ... :) - person Jonas Schmid; 11.03.2019
comment
@Jonas, я понимаю, что вы имеете в виду. Честно говоря, я почти поместил это в BaseActivity, но оставил это простым для ответа. - person Cheticamp; 11.03.2019
comment
Привет, если у меня есть фрагменты внутри действия B. Все будет воссоздано снова - person Abraham Mathew; 13.05.2020

Быстрый ответ:

    @Override
    protected void onRestart() {
        super.onRestart();

        recreate();
    }

Вы добавляете приведенные выше коды в свой MainActivity, и он будет работать.

person myworldbox    schedule 11.03.2020
comment
Я знаю, что вы взволнованы, но, пожалуйста, постарайтесь держать свой язык под контролем. Считайте, что Stack Overflow больше похож на Википедию, чем на Reddit. - person Dharman; 11.03.2020

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

person raj kavadia    schedule 07.03.2019
comment
Спасибо @raj. Я хотел бы предотвратить использование статических переменных. - person Jonas Schmid; 07.03.2019

Полное обновление вашего бэк-стека, вероятно, является излишним и может добавить некоторые накладные расходы / задержку в UX; и, как вы упомянули, у большинства приложений не будет доступа к полному статическому фоновому стеку.

По сути, вы описываете более общую проблему: глобальные изменения темы или самого WindowManager влияют на последующее рисование видов. Но предыдущие макеты для действий в стеке не могут быть перерисованы. Это может показаться вам странным в этой ситуации, но также может быть много веских причин, по которым не следует перерисовывать Activity в стеке, если однажды пользователь вернется к нему. Так что это не автоматическая функция.

Я могу придумать пару вариантов:

1) Напишите собственный класс, унаследованный от Activity, который аннулирует все его представления, когда он снова перемещается в начало стека. Например. в onResume() или onRestart() позвоните (если в Fragment)

View view = getActivity().findViewById(R.id.viewid);
view.invalidate();

Используйте это настраиваемое действие для всех ваших занятий, которые вы хотите сохранить в соответствии с текущим дневным / ночным режимом.

2) Используйте ActivityLifecycleCallbacks. Это помогает сохранить всю вашу логику в одном месте и избавляет от необходимости настраивать наследование, как указано выше. Вы можете аннулировать свои просмотры здесь по мере необходимости, так как деятельность приостанавливается / возобновляется. Вы можете включить Listener, если это ваше приложение меняет тему, и записать, например, как SharedPreference.

Чтобы использовать, добавьте обратные вызовы в свой класс Application:

registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

    @Override
    public void
    onActivityCreated(Activity activity, Bundle savedInstanceState) {
        //can check type of Activity for custom behaviour, if using inheritance
        if(activity instanceof MainActivity) {
           mMainActivities.put(activity, new MainActivityEntry((MainActivity)activity));
            //...
        }
    }

    @Override
    public void
    onActivityDestroyed(Activity activity) {

    } 

    @Override
    public void
    onActivityPaused(Activity activity) {
    }

    @Override
    public void
    onActivityResumed(Activity activity) {
        if(activity instanceof MainActivity) {
        //...
        }
        //can update Entry properties too
        final MainActivityEntry activityEntry = mMainActivities.get(activity);

        if(activityEntry != null) {
        //record state /perform action
        }
    }

    @Override
    public void
    onActivitySaveInstanceState(Activity activity, Bundle outState) {


    }

    @Override
    public void
    onActivityStarted(Activity activity) {


    }

    @Override
    public void
    onActivityStopped(Activity activity) {
    }
});
person dr_g    schedule 11.03.2019