Let's say I have an application with 2 themes: masculine and feminine. The themes simply change out the color palette and a few drawables to appeal to the user's preferred tastes.
Many thanks to http://www.androidengineer.com/2010/06/using-themes-in-android-applications.html for his hints at making that work.
But now lets say I want to get a little cuter with the app and not only change the colors and drawables, but I also want to change the strings. For instance, I might want to add a pirate theme and then "Submit" would be "Arrrrgh!"
So, my basic question is: How can I change the strings throughout my app via user selectable themes?
Edit:
Making this up: the app has 12 buttons and 32 text views I'd like to have theme dependent and I'd like to accomplish this without a giant mapping or a slew of custom attrs.
All 3 of the current solutions will work. Looking for something cleaner though I don't know that such a beast exists.
Strings don't clutter up your application code, leaving it clear and easy to maintain.
A string resource provides text strings for your application with optional text styling and formatting. There are three types of resources that can provide your application with strings: String. XML resource that provides a single string.
String Array. XML resource that provides an array of strings. Quantity Strings (Plurals) XML resource that carries different strings for pluralization. All strings are capable of applying some styling markup and formatting arguments.
String. xml file present in the values folder which is sub folder of res folder in project structure.In Android Studio, we have many Views such as TextView,Button,EditText,CheckBox,RadioButton etc. These views have a common attribute named text.
Yes, it can be done, and here's how: first you'll have to define a theme attribute, like so:
<attr name="myStringAttr" format="string|reference" />
Then, in your themes, add this line
<item name="myStringAttr">Yarrrrr!</item>
or
<item name="myStringAttr">@string/yarrrrr</item>
You can then use this attribute in an XML file like so (note the ?
instead of @
).
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="?attr/myStringAttr" />
or, from code, like so:
public CharSequence resolveMyStringAttr(Context context)
{
Theme theme = context.getTheme();
TypedValue value = new TypedValue();
if (!theme.resolveAttribute(R.attr.myStringAttr, value, true)) {
return null;
}
return value.string;
}
Let's say I have an application with 2 themes: masculine and feminine. The themes simply change out the color palette and a few drawables to appeal to the user's preferred tastes.
How about we pretend that you're doing something else? This is a design anti-pattern, associating particular colors based on gender (e.g., "girls like pink").
This is not to say that your technical objective is bad, just that this is a really stereotypical example.
For instance, I might want to add a pirate theme and then "Submit" would be "Arrrrgh!"
Only if "Cancel" maps to "Avast!".
How can I change the strings throughout my app via user selectable themes?
You have not said where those strings are coming from. Are they string resources? Database entries? Ones that you are retrieving from a Web service? Something else?
I will assume for the moment that these are string resources. By definition, you will need to have N copies of the strings, one per theme.
Since gender and piratical status are not things tracked by Android as possible resource set qualifiers, you can't have those string resources be in different resource sets. While they could be in different files (e.g., res/values/strings_theme1.xml
), filenames are not part of resource identifiers for strings. So, you will wind up having to use some sort of prefix/suffix to keep track of which strings belong with which themes (e.g., @string/btn_submit_theme1
).
If these strings are not changing at runtime -- it's just whatever is in your layout resource -- you could take a page from Chris Jenkins' Calligraphy library. He has his own subclass of LayoutInflater
, used to overload some of the standard XML attributes. In his case, his focus is on android:fontFamily
, where he supports that mapping to a font file in assets.
In your case, you could overload android:text
. In your layout file, rather than it pointing to any of your actual strings, you could have it be the base name of your desired string resource, sans any theme identifier (e.g., if the real strings are @string/btn_submit_theme1
and kin, you could have android:text="btn_submit"
). Your LayoutInflater
subclass would grab that value, append the theme name suffix, use getIdentifier()
on your Resources
to look up the actual string resource ID, and from there get the string tied to your theme.
A variation on this would be to put the base name in android:tag
instead of android:text
. android:text
might point to one of your real string resources, to help with GUI design and such. Your LayoutInflater
would grab the tag and use that to derive the right string at runtime.
If you will be replacing text with other text pulled from theme-based string resources, you could isolate your get-the-string-given-the-base-name logic into a static utility method somewhere that you could apply.
While getting this right initially will take a bit of work, it will scale to arbitrary complexity, in terms of the number of affected UI widgets and strings. You still have to remember to add values for all themes for any new strings you define (bonus points for creating a custom Lint check or Gradle task for validating this).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With