أنيسيتو روي
أنيسيتو روي

Reputation: 45

How to Change android app theme in runtime?

I'm trying to change my app theme during runtime

I've my ChangeTheme class and Utility(SharedPreferences) class

ChangeTheme.java

public class ChangeTheme extends AppCompatActivity {

    private final static int THEME_LIGHT = 1;
    private final static int THEME_DARK = 2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.change_theme_activity);
        updateTheme();
    }



    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.myoptions,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        int id = item.getItemId();

        switch (id){
            case R.id.theme:
                Intent toChangeTheme = new Intent(ChangeTheme.this,ChangeTheme.class);
                startActivity(toChangeTheme);
        }

        return true;
    }

    public void onRadioButtonClicked(View view) {
        // Is the button now checked?
        boolean checked = ((RadioButton) view).isChecked();

        // Check which radio button was clicked
        switch(view.getId()) {
            case R.id.lightTheme:
                if (checked){
                    setTheme(R.style.AppTheme);
                }

                    break;
            case R.id.darkTheme:
                if (checked){
                    setTheme(R.style.AppThemeDark);
                }

                    break;
        }
    }

    public void updateTheme() {
        if (Utility.getTheme(getApplicationContext()) <= THEME_LIGHT) {
            setTheme(R.style.AppTheme);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary));
            }
        } else if (Utility.getTheme(getApplicationContext()) == THEME_DARK) {
            setTheme(R.style.AppThemeDark);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                getWindow().setStatusBarColor(getResources().getColor(R.color.colorSecondary));
            }
        }
    }

}

change_theme_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="12dp"
    android:paddingLeft="8dp"
    android:paddingRight="8dp">

    <TextView
        android:id="@+id/textTheme"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change Theme To:"
        android:layout_marginTop="125dp">
    </TextView>

    <RadioGroup
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginTop="20dp">
        <RadioButton
            android:id="@+id/lightTheme"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Light Theme"
            android:onClick="onRadioButtonClicked"/>
        <RadioButton
            android:id="@+id/darkTheme"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Dark Theme"
            android:onClick="onRadioButtonClicked"/>
    </RadioGroup>

</LinearLayout>

Utility.java

public class Utility {
    public static void setTheme(Context context, int theme) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        prefs.edit().putInt(context.getString(R.string.prefs_theme_key), theme).apply();
    }
    public static int getTheme(Context context) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        return prefs.getInt(context.getString(R.string.prefs_theme_key), -1);
    }
}

styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppThemeDark" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorSecondary</item>
        <item name="colorPrimaryDark">@color/colorSecondaryDark</item>
        <item name="colorAccent">@color/colorSecondaryAccent</item>
    </style>

</resources>

When i click in the buttons to change the theme nothing happens, why? Can you help me? I've already checked similar questions but nothing've helped

Update 2020/09/08

ChangeTheme.java

public class ChangeTheme extends AppCompatActivity {

    private final static int THEME_LIGHT = 1;
    private final static int THEME_DARK = 2;
    private RadioButton lightThemeButton;
    private RadioButton darkThemeButton;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.change_theme_activity);

        lightThemeButton = (RadioButton) findViewById(R.id.lightTheme);
        darkThemeButton = (RadioButton) findViewById(R.id.darkTheme);
    }



    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.myoptions,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        int id = item.getItemId();

        switch (id){
            case R.id.theme:
                Intent toChangeTheme = new Intent(ChangeTheme.this,ChangeTheme.class);
                startActivity(toChangeTheme);
        }

        return true;
    }

    public void onRadioButtonClicked(View view) {
        // Is the button now checked?
        boolean checked = ((RadioButton) view).isChecked();

        // Check which radio button was clicked
        switch(view.getId()) {
            case R.id.lightTheme:
                if (checked){
                    Utility.changeToTheme(this, Utility.THEME_LIGHT);
                }
                break;
            case R.id.darkTheme:
                if (checked){
                    Utility.changeToTheme(this, Utility.THEME_DARK);
                }
                break;
        }
    }
}

Utility.java

public class Utility {

    private static int sTheme;
    public final static int THEME_LIGHT = 0;
    public final static int THEME_DARK = 1;

    //Set the theme of the Activity, and restart it by creating a new Activity of the same type.
    public static void changeToTheme(Activity activity, int theme)
    {
        sTheme = theme;
        activity.finish();
        activity.startActivity(new Intent(activity, activity.getClass()));
    }

    //Set the theme of the activity, according to the configuration. */
    public static void onActivityCreateSetTheme(Activity activity)
    {
        switch (sTheme)
        {
            default:
            case THEME_LIGHT:
                activity.setTheme(R.style.AppTheme);
                break;
            case THEME_DARK:
                activity.setTheme(R.style.DarkTheme);
                break;
        }
    }
}

styles.xml

<color name="color_primary_grey">#212121</color>
<color name="color_primary_black">#000000</color>
<color name="color_accent_white">#aeaeae</color>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<style name="DarkTheme" parent="AppTheme">
    <item name="colorPrimary">@color/color_primary_grey</item>
    <item name="colorPrimaryDark">@color/color_primary_black</item>
    <item name="colorAccent">@color/color_accent_white</item>
</style>

What i've to do now so i can change the theme in runtime?

Upvotes: 2

Views: 8756

Answers (1)

cewaphi
cewaphi

Reputation: 410

I did that before for an application of mine.

For my solution I followed the approach presented in this guide.

Here are the steps to take:

  1. persist the name (or ID) of the theme in the shared preferences. You did that already
  2. in the onCreate method of your activity get this string/ID of the theme (defined in the styles) that should be applied like shown below:
        // inside activities onCreate method
        // Within your switch command each case should look like this
        theme.applyStyle(R.style.AppColorsDefault, true) // in Java it should be getTheme()
        // for other cases just replace the style
        theme.applyStyle(R.style.AppColorsDark, true) // true forces overwriting attributes that are -potentially - defined by your AppTheme 
  1. Restart your activity in order for your changes to take place (themes can only be set in your onCreate method, thus you need to restart your activity)
        val intent = requireActivity().intent // use Intent intent = getActivity().getIntent() in Java
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
        requireActivity().finish() // use getActivity() again
        startActivity(intent)

In my solution you also do not need to inherit the theme. The applied style only needs to contain the changes. Here an example:

styles.xml

<resources>

    <!-- Base application theme. 
         Also consider using a Material Theme
         Theme.MaterialComponents.Light.DarkActionBar
    -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <!-- put all the attributes here that should be applied to the app independent from the applied colors -->
    </style>

    <style name="AppColorsDefault">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>


    <style name="AppColorsDark">
        <item name="colorPrimary">@color/colorSecondary</item>
        <item name="colorPrimaryDark">@color/colorSecondaryDark</item>
        <item name="colorAccent">@color/colorSecondaryAccent</item>
    </style>

</resources>

If your application has more than a single activity and you want your changed style(e.g. colors) to be applied to all of them: Simply create BaseActivity that extends AppCompatActivity and let all your activities extend this BaseActivity

Upvotes: 4

Related Questions