Search code examples
javaandroidstylesthemestextcolor

Using setTheme() at runtime only changes text color


I'm trying to implement a dark theme to my app. The user can easily change between normal and dark in an options menu – which works fine. But when the theme is changed at runtime, only the text color changes and I don't know why.

My dark theme in styles.xml:

<style name="Dark" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/dark_background</item>
    <item name="colorPrimaryDark">@color/dark_top</item>
    <item name="colorAccent">@color/dark_button</item>
    <item name="colorButtonNormal">@color/dark_button</item>
    <item name="android:colorBackground">@color/dark_background</item>
    <item name="android:itemBackground">@color/dark_background</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:textColorHint">#EAEAEA</item>
    <item name="android:textColorPrimary">@color/white</item>
    <item name="android:textColorSecondary">@color/white</item>
    <item name="android:textColorTertiary">@color/white</item>
</style>

My way of setting the style:

setTheme(R.style.Dark);

Before changing theme: before

After changing theme: after

I don't really know why. Is it because of the NavigationView?


Solution

  • Make sure you are calling setTheme() before setContentView() or inflating a view. According to the documentation you must use setTheme() before any views are instantiated in the Context. Use recreate() to create a new instance of your activity so you can apply the changed theme in the onCreate() method.

    You can find several examples of theme switching if you search around a little bit. This is a link to one such example: https://gist.github.com/alphamu/f2469c28e17b24114fe5

    I use PreferenceManager to store settings like this for easy access if I have more than one activity that will need to use the setting. Unless you already have a better way to store your users' theme choice, I would suggest something like the following examples.

    Example MyAppPreferences class:

    public class MyAppPreferences {
    
        private static SharedPreferences getPrefs(Context context) {
            return PreferenceManager.getDefaultSharedPreferences(context);
        }
    
        public static int getThemeId(Context context, int defaultThemeId) {
            return getPrefs(context).getInt("CurrentThemeId", defaultThemeId);
        }
        public static void setThemeId(Context context, int value) {
            getPrefs(context).edit().putInt("CurrentThemeId", value).commit();
        }
    
    }
    

    Example Activity class using the MyAppPreferences class:

    public class MyActivity extends AppCompatActivity implements OnClickListener {
    
        private Button btnDark;
        private Button btnLight;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);            
            // Set the theme
            // If there is nothing set, the light theme will be used by default
            setTheme(MyAppPreferences.getThemeId(this, R.style.Light));
            setContentView(R.layout.myLayout);
    
            btnDark = (Button) this.findViewById(R.id.viewbtnDark);
            btnDark.setOnClickListener(this);
    
            btnLight = (Button) this.findViewById(R.id.viewbtnLight);
            btnLight.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            // 1. Set the theme preference
            // 2. Recreate the activity to "apply" the theme
            if (v.equals(btnDark)) {
                MyAppPreferences.setThemeId(this, R.style.Dark);
                this.recreate();
            } else if (v.equals(btnLight)) {
                MyAppPreferences.setThemeId(this, R.style.Light);
                this.recreate();
            }
        }
    
    }
    

    Your example theme does not show the windowActionBar or windowNoTitle settings so if you happen to be using a default theme and you do not set these options the same way in your dark theme, you may still encounter crashes. Check Logcat for an error like this: java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor..

    Example Dark theme

    <style name="Dark" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/dark_background</item>
        <item name="colorPrimaryDark">@color/dark_top</item>
        <item name="colorAccent">@color/dark_button</item>
        <item name="colorButtonNormal">@color/dark_button</item>
        <item name="android:colorBackground">@color/dark_background</item>
        <item name="android:itemBackground">@color/dark_background</item>
        <item name="android:textColor">@color/white</item>
        <item name="android:textColorHint">#EAEAEA</item>
        <item name="android:textColorPrimary">@color/white</item>
        <item name="android:textColorSecondary">@color/white</item>
        <item name="android:textColorTertiary">@color/white</item>
    </style>