In this article “android change theme programmatically” we will see how to change the theme dynamically. We want to provide a new setting which will allow one to choose from a list of themes. After the theme is selected, when we go back to our activity, we want the theme to be applied. Also, we will take special care for android change theme programmatically.
Steps include the following:
- If you want to use any custom attributes, define those attributes first.
- If you are relying on colors or dimensions, define the values and associate them with proper names.
- Create few themes. When you group a set of style attributes and give a name to it, it becomes your theme.
- Create your main screen. You should be able to set the layout attributes to the custom attributes defined.
- Apply theme programatically before you call the
super.onCreate()
andsetContentView()
- You will set the theme calling
Activity.setTheme(resource)
Let’s try the above steps on our example.
android change theme programmatically
Define Attributes
We will define few custom attributes using attr
element. It has a name
attribute and format
. This is just like declaring your program variables.
attrs.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="background" format="color" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> </resources>
Define Themes
Now we will create our themes. Before that you can define some style attributes and assign a value. For example, below we define few color
resources.
colors.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="welcome_text_color">#FF0000</color> <color name="sky_blue">#6698FF</color> <color name="green_apple">#629632</color> </resources>
Now we will group our custom attributes together, give a name to the style and assign some values to them. We have created two themes Theme1
and Theme2
.
Each theme consists of textColoe
, background
and textSize
.
styles.xml:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme1"> <item name="textColor">@color/green_apple</item> <item name="background">#F4F0CB</item> <item name="textSize">15sp</item> </style> <style name="Theme2"> <item name="textColor">@color/sky_blue</item> <item name="background">#ffffff</item> <item name="textSize">35sp</item> </style> </resources>
Main Screen
In our main screen, we will refer to our custom attributes using ?attr
, for example ?attr/textColor
. Once a theme is applied, the actual value will replace the placeholder.
welcome.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/background" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <TextView android:id="@+id/welcome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="40dp" android:layout_marginTop="18dp" android:text="@string/welcome" android:textColor="?attr/textColor" /> <Button android:id="@+id/apply_theme_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="3dip" android:background="?attr/textColor" android:paddingBottom="10dip" android:paddingTop="10dip" android:text="@string/OK" android:textColor="?attr/background" /> </LinearLayout>
Theme Settings
In order to know which theme to apply, we will use android’s perferences. We will define a ListPreference
which will display a list of theme names. User will select one of them.
The theme values are specified in string-array
.
preferences.xml:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <ListPreference android:defaultValue="@string/def_theme" android:entries="@array/themes" android:entryValues="@array/themes" android:key="theme" android:title="@string/theme_dialog_title" android:summary="@string/theme_summary" android:dialogTitle="@string/theme_dialog_title"/> </PreferenceScreen>
Our theme values will be defined in themes
string-array
.
strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">JavArticles<;/string> <string name="welcome">Theme Example<;/string> <string-array name="themes"> <item>Theme1<;/item> <item>Theme2<;/item> </string-array> <string name="OK">OK<;/string> <string name="def_theme">Theme1<;/string> <string name="theme_summary">Appearance<;/string> <string name="theme_dialog_title">Theme<;/string> <string name="settings">Settings<;/string> </resources>
Add Themes to Settings
ThemePreferenceActivity
is our PreferenceActivity
. We will set its content view to preferences.xml
using addPreferencesFromResource
The user will chose the theme from the displayed themes. After the selection when we navigate back to the previous activity, we want the theme to be applied which means the activity which called the prefernces activity should get notified of the change when it returns back to the activity.
We will set an OnPreferenceChangeListener
to set a result code. This is done so that the Activity which called the perference activity gets notified and from the result code we know whether the theme preference was changed. The result code will be accessed by the Activity class in onActivityResult
.
ThemePreferenceActivity:
package com.javarticles.android; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.Preference.OnPreferenceChangeListener; public class ThemePreferenceActivity extends PreferenceActivity { public static final int RESULT_CODE_THEME_UPDATED = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); findPreference("theme").setOnPreferenceChangeListener(new RefershActivityOnPreferenceChangeListener(RESULT_CODE_THEME_UPDATED)); } private class RefershActivityOnPreferenceChangeListener implements OnPreferenceChangeListener { private final int resultCode; public RefershActivityOnPreferenceChangeListener(int resultCode) { this.resultCode = resultCode; } @Override public boolean onPreferenceChange(Preference p, Object newValue) { setResult(resultCode); return true; } } }
Main Activity
Before super.onCreate
and setContentView
is called, we read our theme from the preferences and apply using setTheme
method. When we change our theme from settings, the main activity should get notified of the change so that it can restart itself and which in turn will re-apply the new theme. This is important so that the new theme gets applied dynamically.
The trick here is to call the perferences activity using startActivityForResult
.
startActivityForResult(new Intent(this, ThemePreferenceActivity.class), SETTINGS_ACTION);
We pass a request code along with the activity class name. We will get this request code back in the callback onActivityResult()
. This way we will know from which activity we are coming back.
In the main activity’s onActivityResult()
, we will look into the request code and result code. The request code will help us to know something about previous activity. Likewise, we can use the result code to hint about some action that we performed in our previous activity.
MainActivity:
package com.javarticles.android; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; public class MainActivity extends Activity { private int SETTINGS_ACTION = 1; public void onCreate(final Bundle savedInstanceState) { SharedPreferences pref = PreferenceManager .getDefaultSharedPreferences(this); String themeName = pref.getString("theme", "Theme1"); if (themeName.equals("Theme1")) { setTheme(R.style.Theme1); } else if (themeName.equals("Theme2")) { Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show(); setTheme(R.style.Theme2); } Toast.makeText(this, "Theme has been reset to " + themeName, Toast.LENGTH_SHORT).show(); super.onCreate(savedInstanceState); setContentView(R.layout.welcome); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.settings: startActivityForResult(new Intent(this, ThemePreferenceActivity.class), SETTINGS_ACTION); } return super.onOptionsItemSelected(item); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SETTINGS_ACTION) { if (resultCode == ThemePreferenceActivity.RESULT_CODE_THEME_UPDATED) { finish(); startActivity(getIntent()); return; } } super.onActivityResult(requestCode, resultCode, data); } }
Reset Theme Dynamically
Main screen opens with the default Theme.
Let’s change the theme use the settings.
Reset the theme to Theme2.
Theme2 is applied, Main screen look is changed.
Download source code
This was an example about applying themes dynamically. You can download the source code here: themeExample.zip