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

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

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

enter image description here

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

Ответы

Ответ 1

@Александр Хансен отвечает в основном ответил на это... Не знаю, почему это не было принято... Возможно, из-за финиша()/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();
    }
});

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

Ответ 2

Переход/анимация автоматически изменяет тему при перезапуске активности, и это можно сделать, добавив в ваши темы элементы "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

Ответ 3

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