I want my activity to have two possible themes, say Theme_Holo and Theme_Holo_Light, as selected by the user. I need to programmatically draw things like horizontal dividers in this activity. The color of the divider should depend on the selected theme. How can I do that easily?
Ideally there should be a name for the standard color of a divider irrespective of the theme used, and the actual RGB realization of that color name would match the selected theme automatically. Is there such thing? It seems unlikely to me that the programmer needs to hardcode RBG values.
Of course, the divider is only an example. I would also like to name the color of EditText, or other widgets, in a way that does not depend on the theme.
In your Activity's onCreate() method(s), before calling setContentView(), set the theme using this.setTheme(customTheme);
or give it a try, Using Themes in Android Applications
You can create your own theme attributes and use them in your app's themes, and allow the user to switch between your app's themes.
First, make a file called attrs.xml in /res/values folder and define some theme attributes:
<resources>
<attr name="myDividerColor" format="color" />
</resources>
Next, make two themes in your /res/values/styles.xml (or in /res/values/themes.xml if you do your themes and styles separately). One theme extends Android's dark theme and one extends Android's light theme. Add your custom attributes to your themes:
<resources>
<!-- Dark theme -->
<style name="AppTheme_Dark" parent="#android:Theme.Holo">
...
<item name="myDividerColor">#color/divider_dark</item>
</style>
<!-- Light theme. -->
<style name="AppTheme_Light" parent="#android:Theme.Holo.Light">
...
<item name="myDividerColor">#color/divider_light</item>
</style>
</resources>
Note that I used name="myDividerColor", NOT android:name="myDividerColor"
Finally, in your Activity code you can get the color as follows:
// the attrs you want
int[] attrs = {R.attr.myDividerColor};
// get attr values for the current theme
TypedArray a = obtainStyledAttributes(attrs);
// first arg is the index of the array, in same order as attrs array above
// second arg is a default value (if not defined or not a resource)
int dividerColor = a.getColor(0, Color.TRANSPARENT);
Related
Let's say I have the following custom theme declared in themes.xml:
<style name="Theme.Custom.Light" parent="#style/Theme.Sherlock.Light">
<item name="android:actionBarTabStyle">#style/Widget.Custom.Light.ActionBar.TabView</item>
<item name="android:actionBarTabTextStyle">#style/Widget.Custom.Light.ActionBar.TabText</item>
<item name="android:actionMenuTextColor">#color/ab_item_text</item>
<item name="android:actionMenuTextAppearance">#style/TextAppearance.Custom.Light.Widget.ActionBar.Menu</item>
</style>
From the application context, we are able to get the Theme class currently applied using
Theme myTheme = appContext.getTheme();
and also, we are able to get the theme's resource id using:
int themeResId = appContext.getApplicationInfo().theme;
What I want
From my code, I would like to check programmatically which is the parent theme of the theme I'm using in order to differentiate between Sherlock, Sherlock.Light & Sherlock.Light.DarkActionBar.
In the example above, I would like to know that I am using the Light variation of the Sherlock theme.
Note: You may wonder why I need to check the parent if I declared it in the xml. Reason is that I'm in a particular situation in which I actually won't know, but this goes beyond the scope of this question.
Theme myTheme = appContext.getTheme();
You can get its parent class using
Theme myThemeParent = appContext.getTheme().getClass().getSuperclass();
and compare it with Sherlock.getclass() to verify if it is the parent. Likewise for other comparisons.
Since DrawerLayout is part of the chrome of an Android activity, a likely background color for the drawer would seem to be the background color of the action bar, so they match. Hence, I'd like to set up the ListView that is the drawer contents to have the same background color as the action bar, and I'd like to do that in the layout XML that defines the ListView.
And here I get lost in the maze of twisty little passages that is the Android style system...
I know that ?android:attr/ syntax allows you to refer, by reference, to a value defined in the theme being used by the activity (e.g., ?android:attr/activatedBackgroundIndicator as the background for a list item row to work with the "activated" state).
I know that android:actionBarStyle is where a theme points to the style to be used to style the action bar itself, and on that nested(?) style, android:background is the background used for the action bar.
What I don't know is how to craft an ?android:attr/, to be applied to a ListView, that pulls the background from the action bar's defined style.
Is this possible? If so, what's the syntax?
My guess is that this is not possible, which is why the official DrawerLayout sample hard-codes the background color...
Thanks!
It's not possible unfortunately via XML.
You could do the following in code (untested, but should work):
// Need to manually create android.styleable.ActionBar.
// If you need other attributes, add them
int[] android_styleable_ActionBar = { android.R.attr.background };
// Need to get resource id of style pointed to from actionBarStyle
TypedValue outValue = new TypedValue();
getTheme().resolveAttribute(android.R.attr.actionBarStyle, outValue, true);
// Now get action bar style values...
TypedArray abStyle = getTheme().obtainStyledAttributes(outValue.resourceId,
android_styleable_ActionBar);
// background is the first attr in the array above so it's index is 0.
Drawable bg = abStyle.getDrawable(0);
abStyle.recycle();
AFAICT it's simply not possible to use any ?android:attr syntax in order to address actionbar background, simply because there is no such attribute exported in the related attrs.xml
You should add/define such attribute in your custom theme, then use a reference in your styles/layouts. Eg:
-attrs.xml:
<declare-styleable name="CustomTheme">
<attr name="actionbarBackground" format="reference"/>
</declare-styleable>
-themes.xml
<style name="CustomTheme" parent="Theme.Holo.Light">
...
<item name="actionbarBackground">#color/your_fav_color</item>
<item name="android:actionBarStyle">#style/CustomActionbar</item>
...
</style>
-styles.xml
...
<style name="CustomActionbar" parent="#android:style/Widget.Holo.Light.ActionBar">
<item name="android:background">?attr/actionbarBackground</item>
....
</style>
My calculator app consists of 30 buttons. I want to provide themes for the calculator keypad. A theme changes button background (gradients, not image backgrounds) and font. Some themes have the same color for all buttons while some have a color for numbers, another color for operators and so on.
The color change is using selectors from res/drawable/*.xml
How do I change the theme via the code?
Hopefully avoiding typing:
button.setBackground(Drawable background);
button.setTypeface(font);
30 times. And if I have 5 themes, then 30 * 5 * 2 = 300 lines of codes!!
I'm new to this and if there is no other way I'll go with the 150 lines.
Also how do I save the user theme selection? Using preferences?
You can create a custom XML theme which will change all of your XML components. After creating a new theme, go into the Android Manifest file and change the theme. For example:
<activity
android:name="com.myapp.MyActivity"
...
android:theme="#style/MyCustomTheme" />
To create the theme, go to res/values/themes.xml and create a new theme with an identifier:
<resources>
...
<style name="MyCustomTheme" parent="android:style/Theme">
<item name="android:textColorPrimary">#ffff0000</item>
</style>
...
</resources>
By using this method, you can create an extensive library of different themes and change to what theme you want.
NOTE: This is not just for changing the background, but it can also be used to change the theme of the buttons. Visit this website for more information:
http://janrain.com/blog/introduction-to-android-theme-customization/
EDIT: As that user commented, it is possible that you can put the function to change the theme of the button in a for() loop.
For your case, I have derived this from the link above. It will change the texture of the buttons in your XML file rather than in Java.
"Using a Custom Nine-Patch With Buttons
A nine-patch drawable is a special kind of image which can be scaled in width and height while maintaining its visual integrity. Nine-patches are the most common way to specify the appearance of Android buttons, though any drawable type can be used.
Example nine-patch PNG.
Notice the one pixel black lines around the edge, they control the scaling of the image.
Save this bitmap as MyApplication/res/drawable/my_nine_patch.9.png
Define a new style (you can define the new style in the same file that you defined your custom theme from Creating a Custom Android Theme above) …:
<resources>
...
<style name="MyCustomButton" parent="android:Widget.Button">
<item name="android:background">#drawable/my_nine_patch</item>
</style>
...
</resources>
Apply the new button style to the buttonStyle attribute of your custom theme:
<resources>
...
<style name="MyCustomTheme" parent=...>
...
<item name="android:buttonStyle">#style/MyCustomButton</item>
</style>
...
</resources>
Now the buttons in the activities your theme is applied to have custom images. However, you may notice that they don’t change appearance when selected. Read Selector Drawables below for an introduction to using multiple drawables to define one drawable that changes based on state."
From here, you can change certain components of the theme (such as the button texture as an image).
After you have a theme that looks good, apply it in the Android Manifest as I mentioned above.
I will FURTHER edit this if it still does not answer your question.
I've got an app with two themes (dark and light) that can be selected at runtime. This works. I also have a ListView with rows that can have one of three different layouts, each of which has a style (say, different colors). This also works. But I can't get these two features to work together. I really need six different styles, three for one theme (dark) and three for the other (light), but I can't figure out how to choose a style for a list item based on the current theme, or get that effect any other way by using XML files. My three layouts each point to a custom theme that sets the color, but that overrides whatever theme I've got set. Themes can only contain items that are "styleable", so I can't put my own custom items in there. There may be a way to do this programmatically, but I was hoping to do it declaratively. Any ideas?
Thanks to wingman for the hint. My situation involved colors, which are a bit more complicated, so I'll write up my solution here.
I have two themes (light and dark) which the user can choose from in the Settings screen. I have a ListView which can have two types of rows (plain and note), each with its own styling. Firstly each layout needs to point to a style:
<TextView style="#style/PlainItemText" ... />
(or NoteItemText) and we need to define the styles:
<style name="PlainItemText">
<item name="android:textSize">#dimen/list_item_font_size</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?plainTextColor</item>
</style>
The text color can't be fixed because it depends on the selected theme. We must create a custom attribute and refer to it with a question mark, as above. We define the attribute in res/values/attrs.xml:
<!-- Attributes we use to set the text color of the various list items. -->
<attr name="plainTextColor" format="reference|color"/>
<attr name="noteTextColor" format="reference|color"/>
We can then define the various colors. Here we have two styles and two themes, so we need four color state lists, each in its own file under res/color. For example, here's res/color/plain_text_color_dark.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:color="#android:color/white"/>
<item android:state_selected="true" android:color="#android:color/black"/>
<item android:state_focused="true" android:color="#android:color/black"/>
<item android:state_pressed="true" android:color="#android:color/black"/>
<item android:color="#android:color/white"/>
</selector>
The selected/focused/pressed colors are the same in all these files because they're over the highlight color. Be careful with the state_window_focused version. It didn't behave as advertised, and I had to set it to the default color (the last line above) in all cases. Now we need to create our themes and bind the attributes to one of the colors. These lines go into res/values/themes.xml:
<style name="Theme.Dark" parent="android:Theme">
<item name="plainTextColor">#color/plain_text_color_dark</item>
<item name="noteTextColor">#color/note_text_color_dark</item>
</style>
<style name="Theme.Light" parent="android:Theme.Light">
<item name="plainTextColor">#color/plain_text_color_light</item>
<item name="noteTextColor">#color/note_text_color_light</item>
</style>
Finally we pick a theme at run-time, in an Activity's onCreate() method, before calling super.onCreate():
if (isDarkTheme) {
activity.setTheme(R.style.Theme_Dark);
} else {
activity.setTheme(R.style.Theme_Light);
}
Note that I don't take into account newer themes like Holo, so my app looks old on Honeycomb and later. I'll fix that at some point, but it wasn't a regression here.
A twist in my case is that some Activities have a larger title bar in order to fit some buttons. In principle I should have created four themes, a light and dark for a narrow title and a light and dark for a fat title. But instead I created a mix-in style:
<!-- Mix-in style for activities. -->
<style name="ButtonTitleBar">
<item name="android:windowTitleSize">44dp</item>
</style>
and procedurally add it to whatever theme I'm using. This code goes right after the above setTheme() calls:
if (buttonTitleBar) {
// Mix in this other style.
Resources.Theme theme = activity.getTheme();
theme.applyStyle(R.style.ButtonTitleBar, true);
}
I didn't see this documented anywhere, and I don't know if it's legit, but the code of Activity.getTheme() implies that it should work fine, and it has worked in all my testing. This can help avoid the combinatorial explosion of themes that you can find in the standard Android theme list.
It's a long time ago that Lawrence Kesteloot published his solution in 2012. Now it is six years later, a am new in Android and try to solve the similar problem:
How can I exchange the whole style of the application by just exchanging one theme?
This is a generalisation of Lawrences issue how to organise two exchangeable themes.
I figured out a solution based on Lawrence's and going a step further.
(Not claiming it is the perfect solution, yet an improvement.)
Lawrence figured out the power of user defined attributes to reach this goal. He uses them to address colours depending on the the currently selected theme.
While this is working it still requires to define attributes for each and every property. It doesn't scale well. So why not bundling the properties into styles and themes and using the same mechanism?
This results in a master theme, that is defining child themes and styles.
res/values/attrs.xml
<resources>
...
<attr name="mainViewTheme" format="string"/>
<attr name="asideViewTheme" format="string"/>
...
</resources>
When defining the attribute to set a theme, there is no special format for it. The format string does it.
res/values/styles.xml
<style name="MasterTheme">
...
<item name="mainViewTheme">#style/MainViewTheme</item>
<item name="asideViewTheme">#style/AsideViewTheme</item>
...
</style>
<style name="MainTextTheme">
...
</style>
<style name="MainViewTheme">
...
</style>
res/layouts/main.xml
<TextView
android:theme="?mainViewTheme"
...
By exchanging the master theme all styles are adjusted. It still requires the definition of a handful of theme attributes and then does a powerful job. Setting up attributes for every property is not required any more.
What is the best method to set a widget colors theme saved on SharedPreferences. Users choose their preferred colors theme and programatically I set the theme.
For example I have two styles for a TextView with id "widgetThemeDate":
<style name="widgetThemeDate" parent="#style/widgetThemeLight">
<item name="android:textColor">#color/colorWhite</item>
</style>
<style name="widgetThemeDate" parent="#style/widgetThemeDark">
<item name="android:textColor">#color/colorBlack</item>
</style>
In this case I receive the following error: "error: Resource entry widgetThemeDate already has bag item android:textColor."
How can I set "widgetThemeDate" color depending user's choice?
You can't set whole themes on a view programatically (see this discussion), but you can change certain aspects progamatically. For instance. you case you could use the setTextColor() method to change the color of text programatically on ay TextView.