Support multiple themes in an Android application

Support multiple themes in an Android application


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

demo_two_themes

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

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 steles.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 Main Activity 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.

CategoriesUncategorized

13 thoughts on “Support multiple themes in an Android application

    1. Unfortunately, right now all code just in article. However, if you need it, yet. I can share it. Please let me know if you need it right now.

  1. Just as a side note.
    Applying a theme runtime will cause android to recursively apply and override style attributes. This is fine, but it consumes memory.
    I had quite some out of memory reports coming in from ancient devices.
    Another solution is to define multiple activities where:

    the default activity is just a loader activity. its checks which theme is stored in the sharedpreferences.
    Based on that info it loads either activityA or activityB
    ActivityB is identical to ActivityA (just an extension) With the difference that inside the manifest.xml you define the themes for both Activities.

    I know its not so elegant, but if you ever run into similar issues as what i have you can try this option.

  2. how to change entire application theme…..your code is changing only current activity…if we go to another page/activity it is showing the default theme which we mentioned in manifest default theme

    1. You have few options, first of all, you can set Theme for the whole app in Android:

      < application
          ...
          android:theme="@style/AppTheme.Blue" >
      

      You can set up the Theme for the Activity:

      < activity 
          android:name=".MainActivity"
          android:theme="@style/AppTheme.Blue ">
      

      And you can do it from the source code of Activity:

      @Override
      public void onCreate(Bundle savedInstanceState) {
          setTheme(R.style.AppTheme)
          super.onCreate(savedInstanceState)
          ...
      }
      
  3. Thank you for this simple example. One question though when using lighter colours for the appbar, the text is white and not very readable. How and where would I use primaryTextColor to change this. I have tried several ways and I cannot seem to get this aspect of the them to work correctly.

    1. Hi Robert, thank you for your question. I don’t know what are you using during the development process. However, I recommend using the Toolbar. You can create a style for the Toolbar:

      %MINIFYHTML2f2244694b1b38a82aafe1eb5557b60212%

      After it, this style can be added to basic style:

      %MINIFYHTML2f2244694b1b38a82aafe1eb5557b60213%

Leave a Reply

Your email address will not be published. Required fields are marked *