How to switch themes (night mode) without restarting the activity?

AndroidAndroid Theme

Android Problem Overview


I have made a few apps that support multiple themes, but I always had to restart the app when user switches theme, because setTheme() needs to be called before setContentView().

I was okay with it, until I discovered this app. It can seamlessly switch between two themes, and with transitions/animations too!

enter image description here

Please give me some hints on how this was implemented (and animations too). Thanks!

Android Solutions


Solution 1 - Android

@Alexander Hanssen's answer basically has answered this... Don't know why it was not accepted... Maybe because of the finish()/startActivity(). I voted for it and I tried to comment but cannot...

Anyway, I would do exactly what he described in terms of styles.

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

But instead of finish/start with new intent:

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

I would do:

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

The effect is as shown in the video...

Solution 2 - Android

The transition/animation makes the theme change seamless when you restart the activity, and this can be done by adding the items "android:windowanimationStyle" to your themes, and then referencing a style where you specifiy how the Activity should animate when it enters and exits. Note that this makes the animation apply on all activities with that theme.

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

Then, when you want to change theme you could do this when clicking a button:

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

Then in your onCreate method, use the setTheme() to apply the theme that is currently set in AppSettings like this:

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

Check out this gist for reference: https://gist.github.com/alphamu/f2469c28e17b24114fe5

Solution 3 - Android

for those who are trying to find solution for android version 10 or updated.

to set dark/light mode use this:

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

it will change the display of your app but with a flicker

to avoid the activity recreation flicker (for smooth transition), in your activity add the below method

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

Solution 4 - Android

There isn't anything preventing you from calling setTheme() and then setContentView() again. You'll just need to restructure your app a bit so that, if you change the theme, you need to reinitialize any member variables you might have that are holding references to View objects.

Solution 5 - Android

setTheme() before super.onCreate(savedInstanceState) in GKA answer is perfect approach and work well, thanks to GKA.

but it creates new instances for all resources again, including activities, fragments, and recycler views. I think it may be heavy work and cause to loss of some saved data like local variables.

>accourding to google document: https://developer.android.com/reference/android/app/Activity#recreate() > > Cause this Activity to be recreated with a new instance. This results > in essentially the same flow as when the Activity is created due to a > configuration change -- the current instance will go through its > lifecycle to onDestroy() and a new instance then created after it.

there is another approach that you can change the theme programmatically with code (Java or Kotlin), in this approach you don't need to recreate all resources, and also you can use custom animation like ripple.

check my GitHub library: https://github.com/imandolatkia/Android-Animated-Theme-Manager

in this library, you can create your custom themes and change them dynamically with ripple animation without recreating any resources.

enter image description here

Solution 6 - Android

Simply efficient one liner in fragment:

requireActivity().recreate();

For activity:

recreate();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1032613View Question on Stackoverflow
Solution 1 - AndroidGKAView Answer on Stackoverflow
Solution 2 - AndroidAlexander HanssenView Answer on Stackoverflow
Solution 3 - AndroidAsadView Answer on Stackoverflow
Solution 4 - AndroidDavid WasserView Answer on Stackoverflow
Solution 5 - AndroidIman DolatkiaView Answer on Stackoverflow
Solution 6 - AndroidAli NawazView Answer on Stackoverflow