Android

Support multiple themes in an Android application

Alex Zhukovich 4 min read
Support multiple themes in an Android application
Table of Contents

In this post we will talk about supporting multiple themes in Android application.

Multiple themes in Android app

We will create demo application with 2 themes. User can choose of of this theme for whole application. Theme in our application will change background color, status bar color, etc.

Theme colors

This image show different style tags and meaning for Android 5.+.

Firstly need to add dependency for build.grade file in our project.

dependencies {
    compile 'com.android.support:appcompat-v7:25.2.0'
}

For support changing background theme for Android version less 5 we can add own attribute called backgroundColor for it need to add file attrs.xml to /res/values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="backgroundColor" format="color"/>
</resources>

Next step is create different themes. For it we can use styles.xml or themes.xml files. In this case I used to styles.xml file.

<resources>
    <style name="AppTheme.Blue" parent="Theme.AppCompat">
        <item name="colorPrimary">@color/primaryColor_blue</item>
        <item name="colorPrimaryDark">@color/primaryColorDark_blue</item>
        <item name="colorAccent">@color/primaryAccent_blue</item>
        <item name="backgroundColor">@color/primaryColorDark_blue</item>
    </style>
    <style name="AppTheme.Red" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/primaryColor_red</item>
        <item name="colorPrimaryDark">@color/primaryColorDark_red</item>
        <item name="colorAccent">@color/primaryAccent_red</item>
        <item name="backgroundColor">@color/primaryColorDark_red</item>
    </style>
</resources>

After it need to add all colors to colors.xml file (/res/values/).

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primaryColor_blue">#2196F3</color>
    <color name="primaryColorDark_blue">#1976D2</color>
    <color name="primaryAccent_blue">#E3F2FD</color>

    <color name="primaryColor_red">#F44336</color>
    <color name="primaryColorDark_red">#D32F2F</color>
    <color name="primaryAccent_red">#FFEBEE</color>
</resources>

Next step is update layout for MainActivity. In my case it's a activity_main.xml (/res/layout/).

<?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"
    android:background="?attr/backgroundColor">
    <Button
        android:id="@+id/blue_theme_btn"
        android:text="BLUE"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/red_theme_btn"
        android:text="RED"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

When we used tag android:background="?attr/backgroundColor" it's our own tag, which we created in attrs.xml file. This tag depends of theme which active right now.

Our theme must saved after close app. For we will use SharedPreferences. I created class called Utility.java which contain next code.

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

For correct work this class need to add string constant to strings.xml file (/res/values/).

<resources>
    <string name="app_name">MultipleThemesApp</string>

    <string name="prefs_theme_key">theme</string>
</resources>

We can check and update theme in our MainActivity, but better way is create BaseActivity, which will check and init our theme for activity.

public class BaseActivity extends AppCompatActivity {

    private final static int THEME_BLUE = 1;
    private final static int THEME_RED = 2;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        updateTheme();
    }
    public void updateTheme() {
        if (Utility.getTheme(getApplicationContext()) <= THEME_BLUE) {
            setTheme(R.style.AppTheme_Blue);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                getWindow().setStatusBarColor(getResources().getColor(R.color.primaryColorDark_blue));
            }
        } else if (Utility.getTheme(getApplicationContext()) == THEME_RED) {
            setTheme(R.style.AppTheme_Red);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                getWindow().setStatusBarColor(getResources().getColor(R.color.primaryColorDark_red));
            }
        }
    }
}

We must extend MainActivity from BaseActivity and add listeners to click button for change theme and recreate activity.

public class MainActivity extends BaseActivity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((Button)findViewById(R.id.blue_theme_btn)).setOnClickListener(this);
        ((Button)findViewById(R.id.red_theme_btn)).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.blue_theme_btn:
                Utility.setTheme(getApplicationContext(), 1);
                recreateActivity();
                break;
            case R.id.red_theme_btn:
                Utility.setTheme(getApplicationContext(), 2);
                recreateActivity();
                break;
        }
    }

    public void recreateActivity() {
        Intent intent = getIntent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
        finish();
        overridePendingTransition(0, 0);
        startActivity(intent);
        overridePendingTransition(0, 0);
    }
}

Last step is update AndroidManifest.xml with new default theme:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.alexzh.multiplethemesapp">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.Blue" >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Full source code you can find here.


Mobile dev & testing with Alex Zhukovich

A blog about Android development & testing, Best Practices, Tips and Tricks

Share
Comments
More from Mobile dev & testing with Alex Zhukovich
Jetpack Compose: RadioButton
Jetpack Compose

Jetpack Compose: RadioButton

Many mobile apps ask a user to provide data, and RadioButton component can simplify the onboarding process and how users can fill the information. I'll show you the possibilities of "RadioButton", how to customize and test it in JetpackCompose.
Alex Zhukovich 9 min read

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Mobile dev & testing with Alex Zhukovich.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.