Как переключать темы (ночной режим) без перезапуска активности?

Я сделал несколько приложений, которые поддерживают несколько тем, но мне всегда приходилось перезапускать приложение, когда пользователь переключает тему, потому что setTheme() нужно позвонить раньше setContentView(),

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

Пожалуйста, дайте мне несколько советов о том, как это было реализовано (и анимации тоже). Спасибо!

7 ответов

Решение

Ответ Александра Хансена в основном ответил на это... Не знаю, почему он не был принят... Может быть, из-за finish()/startActivity(). Я проголосовал за это, и я пытался комментировать, но не могу...

Во всяком случае, я бы сделал именно то, что он описал в терминах стилей.

<style name="AppThemeLight" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
    <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
    <item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>

Но вместо того, чтобы закончить / начать с новым намерением:

Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();

Я бы сделал:

@Override
protected void onCreate(Bundle savedInstanceState) {

    // MUST do this before super call or setContentView(...)
    // pick which theme DAY or NIGHT from settings
    setTheme(someSettings.get(PREFFERED_THEME) ? R.style.AppThemeLight : R.style.AppThemeDark);

    super.onCreate(savedInstanceState);
}

// Somewhere in your activity where the button switches the theme
btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        // decide which theme to use DAY or NIGHT and save it
        someSettings.save(PREFFERED_THEME, isDay());

        Activity.this.recreate();
    }
});

Эффект, как показано на видео...

Переход / анимация делают изменение темы плавным, когда вы перезапускаете действие, и это можно сделать, добавив элементы "android: windowanimationStyle" в ваши темы, а затем сославшись на стиль, в котором вы указываете, как должно оживлять действие, когда оно входит и выходы. Обратите внимание, что это позволяет применять анимацию ко всем действиям с этой темой.

<style name="AppThemeLight" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDark" parent="Theme.AppCompat">
    <!-- Customize your theme here. -->
    <item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
</style>
<!-- This will set the fade in animation on all your activities by default -->
<style name="WindowAnimationTransition">
    <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
    <item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>

Затем, когда вы хотите изменить тему, вы можете сделать это, нажав кнопку:

AppSettings settings = AppSettings.getInstance(this);
settings.set(AppSettings.Key.USE_DARK_THEME,
!settings.getBoolean(AppSettings.Key.USE_DARK_THEME));
Intent intent = new Intent(this, <yourclass>.class);
startActivity(intent);
finish();

Тогда в вашем onCreate метод, используйте setTheme() чтобы применить тему, которая в настоящее время установлена ​​в AppSettings, следующим образом:

AppSettings settings = AppSettings.getInstance(this);
setTheme(settings.getBoolean(AppSettings.Key.USE_DARK_THEME) ? R.style.AppThemeDark : R.style.AppThemeLight);
super.onCreate(savedInstanceState);
setContentView(<yourlayouthere>);

Проверьте эту суть для справки: https://gist.github.com/alphamu/f2469c28e17b24114fe5

setTheme() перед super.onCreate(savedInstanceState) в ответе GKA - идеальный подход и хорошо работает, благодаря GKA.

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

в соответствии с документом Google: https://developer.android.com/reference/android/app/Activity#recreate()

Заставить это действие быть воссозданным с новым экземпляром. Это приводит к тому же потоку, что и при создании Activity из-за изменения конфигурации - текущий экземпляр проходит свой жизненный цикл до onDestroy (), а после него создается новый экземпляр.

есть другой подход, в котором вы можете программно изменить тему с помощью кода (Java или Kotlin), при этом вам не нужно воссоздавать все ресурсы, а также вы можете использовать настраиваемую анимацию, такую ​​как рябь.

проверьте мою библиотеку GitHub:https://github.com/imandolatkia/Android-Animated-Theme-Manager

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

для тех, кто пытается найти решение для android версии 10 или обновлённой.

чтобы установить темный / светлый режим, используйте это:

      AppCompatDelegate.setDefaultNightMode(state) //state can be AppCompatDelegate.MODE_NIGHT_YES or AppCompatDelegate.MODE_NIGHT_NO

он изменит отображение вашего приложения, но с мерцанием

чтобы избежать мерцания при восстановлении активности (для плавного перехода), добавьте в свою деятельность следующий метод

      @Override
    public void recreate() {
        finish();
        overridePendingTransition(R.anim.anime_fade_in,
                                  R.anim.anime_fade_out);
        startActivity(getIntent());
        overridePendingTransition(R.anim.anime_fade_in,
                                  R.anim.anime_fade_out);
    }

Просто эффективный однострочник во фрагменте:

      requireActivity().recreate();

По активности:

      recreate();

Ничто не мешает вам звонить setTheme() а потом setContentView() снова. Вам просто нужно немного реструктурировать свое приложение, чтобы при смене темы вам нужно было повторно инициализировать любые переменные-члены, которые могут содержать ссылки на View объекты.

Все приведенные выше ответы на самом деле не препятствуют воссозданию активности! Правильное решение должно быть таким:

  1. Добавьте «uiMode» в поле configChanges вAndroidManifest.xmlчтобы избежать повторного создания активности при переключении темного режима.

Android:configChanges="uiMode"

  1. Обновите цвет всех видов вручную, когда включен темный режим.onConfigureChanged()функция в Activity.

Для получения более подробной информации вы можете обратиться к этой статье:https://proandroiddev.com/daynight-applying-dark-mode-without-recreating-your-app-c8a62d51092d .

Другие вопросы по тегам