I've got an image in two different colors, corresponding to two different themes in Android. I want to use them in my Action Bar. I want to refer to a drawable for the icon, theme agnostic, but have that drawable reference the light color for the light theme, and the dark color for the light theme.
In the menu:
<item
android:id="#+id/menu_attach_existing_picture"
android:icon="#drawable/buttonface_picture"
android:showAsAction="always"
android:title="#string/menu_attach_existing_picture">
</item>
Then in the styles I want to have something that maps #drawable/buttonface_picture to #drawable/buttonface_picture_light in the light theme and #drawable/buttonface_picture_dark for the dark theme.
I feel like there's got to be a way, but I'm having trouble finding the syntax. If it changes anything, I am using ActionBarSherlock.
Seems like I'm always just a Google search or two away from my answers. The solution is:
in styles.xml
<attr name="buttonface_picture" format="reference"/>
then in the actual theme:
<item name="buttonface_picture">#drawable/buttonface_picture_dark</item>
or
<item name="buttonface_picture">#drawable/buttonface_picture_light</item>
Then, in the menu.xml:
<item
android:id="#+id/menu_attach_existing_picture"
android:icon="?attr/buttonface_picture"
android:showAsAction="always"
android:title="#string/menu_attach_existing_picture">
</item>
The Accessing Resources Page combined with this SO eventually got it to click in my brain.
Related
What is best practice to organise styling of buttons of a professional android application? Assume a larger contemporary application (SDK 26+, min SDK 21).
This question is answerable, as both the sources of Material Design and the setup of Android Studio give enough clues and examples of the patterns of the intended professional usage. Surely the user is not limited to this patterns, but following them, makes the application play well together with the sources of Material Design and provides best maintainability.
I find several ingredients related to the styling of buttons.
#style/Widget.AppCompat.Button
#style/Widget.AppCompat.Button.Colored
#style/TextAppearance.AppCompat.Button
#style/TextAppearance.AppCompat.Widget.Button
#color/foreground_material_dark
?colorAccent
?textColorPrimary
?android:colorForeground
?textAppearanceButton
There may be more.
How are all the ingredients related?
How are they intended to be used together in professional theming?
You can look up the sources. However, even knowing all details does not give the full picture of the intended usage. This question is asking to draw the picture.
(Min SDK 21)
General Approach
Granularity
I think it an enough fine-grained approach to separate the text appearance from the backgrounds. This gives the option to combine different backgrounds with different text appearances. It also matches the two style settings provided by Button and the organisation of Material Design. Hence it addresses the question, how it is intended to be used.
The price is, that each Button needs both settings:
Button text: android:textAppearance
Button background: style
To even lower this price styles_material.xml in fact takes an advanced approach. Each button style already includes a default text appearance. So in the normal case I only have to apply the button style.
<Button
style="?defaultButtonStyle"
I follow this pattern for my own button styling, as the question is for the intended usage. If I want to modify the default, I add an alternative text appearance by setting it to android:textAppearance.
<Button
style="?defaultButtonStyle"
android:textAppearance="?smallButtonTextAppearance"
For very special buttons I still can adjust the styling on the level of the layout file. This is the lowest level of granularity.
Hint: Be aware that android:textAppearance has a very low
precedence. If you set a text attribute somewhere in the theme (or style),
you will overwrite the same attribute in all of android:textAppearance.
It works with a similar force like the "!important" annotation in CSS,
which can be a pretty pitfall.
Flexible theming
Without
If I don't plan to use different themes, I can set the styles directly into the layouts.
<Button
style="#style/My.DefaultButtonStyle"
android:textAppearance="#style/My.SmallButtonTextAppearance"
...
With
If a want to be able to exchange themes, I map all types of styles to attributes first. Then I set the styles indirectly by using the attributes. This gives me the option to connect other styles for other themes, without the need to duplicate layouts.
<Button
style="?defaultButtonStyle"
android:textAppearance="?smallButtonTextAppearance"
...
I personally prefer not to use or mix given attributes, but to fully define my own set of attributes addressing my design. So the levels of the onion stay cleanly separated.
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<!-- button text appearance -->
<attr name="defaultButtonTextAppearance" format="reference" />
<attr name="smallButtonTextAppearance" format="reference" />
<!-- button backgrounds -->
<attr name="defaultButtonStyle" format="reference" />
<attr name="alarmButtonStyle" format="reference" />
In the themes the attributes are mapped to theme specific styles.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="OtherTheme" parent="Theme.AppCompat">
<!-- button text appearance -->
<item name="defaultButtonTextAppearance">#style/OtherTheme.DefaultButtonTextAppearance</item>
...
<!-- button backgrounds -->
<item name="defaultButtonStyle">#style/OtherTheme.DefaultButtonStyle</item>
...
Button Text
If I track down the styles to the sources I come to a file data/res/values/styles_material.xml, which defines the root of all button text appearances. TextAppearance.Material.Button inherits from TextAppearance.Material, but the four relevant attributes for buttons are overwritten.
<style name="TextAppearance.Material">
<item name="textColor">?attr/textColorPrimary</item>
<item name="textColorHint">?attr/textColorHint</item>
<item name="textColorHighlight">?attr/textColorHighlight</item>
<item name="textColorLink">?attr/textColorLink</item>
<item name="textSize">#dimen/text_size_body_1_material</item>
<item name="fontFamily">#string/font_family_body_1_material</item>
<item name="lineSpacingMultiplier">#dimen/text_line_spacing_multiplier_material</item>
</style>
<style name="TextAppearance.Material.Button">
<item name="textSize">#dimen/text_size_button_material</item>
<item name="fontFamily">#string/font_family_button_material</item>
<item name="textAllCaps">true</item>
<item name="textColor">?attr/textColorPrimary</item>
</style>
It can be overwritten by my own inherited styles. It also shows, that it would be easy to write my own text appearance style without using inheritance at all.
Color
Understanding Androids text color management system is them most confusing part, because the system is quite powerful. Here comes some enlightenment.
In the above TextAppearance.Material.Button I find text color is specified by the attribute ?textColorPrimary. This attribute again is based on the attribute ?android:colorForeground.
The attribute ?android:colorForeground is the central switch to set the text colors. Be default all text colors are calculated based on this setting, also those of the buttons. For example different grades of greyed-out or opaque variants are calculated for disabled buttons, for body text, etc.
Instead of touching dozens of different places it is a good idea to set the common default text color here and rely on the default Android color calculating system as far as useful. Tweak it in details.
<item name="android:colorForeground">#color/orange_700</item>
This setting defaults to #color/foreground_material_dark.
Hint 1: If you edit the setting by use of
the Android Studio Theme Editor, it will possibly change the
value of#color/foreground_material_dark. To me it does not
feel like a good idea to change a value of material dark because
it is not my realm. Better use a reference like shown before.
Hint 2: The Theme Editor is an appropriate tool to discover
the relations of the color attributes system. This relations reveal,
when you experimentally try to edit the different attributes with the editor.
If I want a button text color that varies from the overall text color, I set it on the level of the text appearance style.
Hint 3: Using ?android:colorForeground does not work out of the
box below API 26. For a workaround see here.
Text size
The text size is the factor of the text appearance, that I typically want to directly adjust to my own design within my customised text appearance styles.
<style name="My.SmallButtonTextAppearance" parent="My.DefaultButtonTextAppearance">
<item name="android:textSize">16sp</item>
</style>
TextAppearance.Material.Button takes the text size default from the resource #dimen/text_size_button_material. There is no system with a central text size setting comparable to the text color setting system.
All caps
The root style TextAppearance.Material.Button set's all caps to true. There is not even a resource, the value is taken from. It's just hard coded.
<item name="textAllCaps">true</item>
There is a high chance, I want to set it to false in my customised button styles.
<item name="android:textAllCaps">false</item>
Font family
As with the text colors the font family typically is a system with a common central nature. How is it managed for the buttons? The root style TextAppearance.Material.Button makes use a the string resource #string/font_family_button_material.
<item name="fontFamily">#string/font_family_button_material</item>
In the file data/res/values/donttranslate_material.xml this is set to sans-serif-medium, while in the file data/res/values-watch/donttranslate_material.xml is is set to sans-serif-condensed.
<string name="font_family_button_material">sans-serif-medium</string>
<string name="font_family_button_material">sans-serif-condensed</string>
This sans-serif settings are mapped to my chosen font family within the fonts setup. Typically sans-serif is fine for button text. For further customisation of the fonts I point to this question.
Button Style
Apart from using a color for the background, a xml resource file can be applied to specify the background with fancy corners, color gradients or other graphical effects, also supporting different backgrounds for different states of the button.
This part is strongly influenced by my design. I will typically use my own background.
On the other hand there is a rich system of predefined button backgrounds resource files in material design. I would like to give a short overview here, but that's beyond my skills and seems so large to be worth a topic of it's own.
The style for the background should not contain settings for width, height or margins, as this belongs into the surrounding layout. On the other hand the padding belongs into the background style.
Button styles of Material Design
In the file data/res/values/styles_material.xml I find nine button styles I may inherit from. If I write my very own, a should not forget to set a default text appearance.
The root element is Widget.Material.Button. It set's the default text appearance to ?textAppearanceButton. Hence, setting this attribute is an option to directly use the material design button styles without inheritance and yet have your customised default text appearance.
<!-- Bordered ink button -->
<style name="Widget.Material.Button">
<item name="background">#drawable/btn_default_material</item>
<item name="textAppearance">?attr/textAppearanceButton</item>
<item name="minHeight">48dip</item>
<item name="minWidth">88dip</item>
<item name="stateListAnimator">#anim/button_state_list_anim_material</item>
<item name="focusable">true</item>
<item name="clickable">true</item>
<item name="gravity">center_vertical|center_horizontal</item>
</style>
The attribute ?colorAccent is used, to set the color of Widget.AppCompat.Button.Colored. See the Android Studio Theme Editor. See #drawable/btn_colored_material.
Note that the default text appearance of Widget.AppCompat.Button.Colored varies and is not set by a customisable attribute.
<!-- Colored bordered ink button -->
<style name="Widget.Material.Button.Colored">
<item name="background">#drawable/btn_colored_material</item>
<item name="textAppearance">#style/TextAppearance.Material.Widget.Button.Colored</item>
</style>
<!-- Small bordered ink button -->
<style name="Widget.Material.Button.Small">
<item name="minHeight">48dip</item>
<item name="minWidth">48dip</item>
</style>
<!-- Borderless ink button -->
<style name="Widget.Material.Button.Borderless">
<item name="background">#drawable/btn_borderless_material</item>
<item name="stateListAnimator">#null</item>
</style>
Note that the default text appearance of Widget.Material.Button.Borderless.Colored varies and is not set by a customisable attribute.
<!-- Colored borderless ink button -->
<style name="Widget.Material.Button.Borderless.Colored">
<item name="textAppearance">#style/TextAppearance.Material.Widget.Button.Borderless.Colored</item>
</style>
Note that Widget.Material.Button.ButtonBar.AlertDialog inherits from Widget.Material.Button.Borderless.Colored. Same limitations of the default text appearance apply.
<!-- Alert dialog button bar button -->
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored">
<item name="minWidth">64dp</item>
<item name="minHeight">#dimen/alert_dialog_button_bar_height</item>
</style>
<!-- Small borderless ink button -->
<style name="Widget.Material.Button.Borderless.Small">
<item name="minHeight">48dip</item>
<item name="minWidth">48dip</item>
</style>
<style name="Widget.Material.Button.Inset">
<item name="background">#drawable/button_inset</item>
</style>
<style name="Widget.Material.Button.Toggle">
<item name="background">#drawable/btn_toggle_material</item>
<item name="textOn">#string/capital_on</item>
<item name="textOff">#string/capital_off</item>
</style>
Personally I would either use one of this predefined button styles or inherit my own from Widget.Material.Button. This keeps the hierarchy of inheritance low and the code easily readable. It saves me at most three lines of code if I inherit from another style, while the code becomes less maintainable.
There are exceptions to this rule of thumb. For example #drawable/btn_borderless_material is private. So I either have to inherit from Widget.Material.Button.Colored or create a copy of the file.
Appendix
Related questions
Attributes
Defining custom attrs
Android Use custom themes to modify style attributes
Android "?colorPrimary" vs "?attr/colorPrimary"?
Inheritance issue, when using property androidTextapperance vs. property style
Colors
Setting a color based on theme
Attribute android:colorForeground not working in API 23
android themes - defining colours in custom themes
Fonts
How to set default font family for entire Android app
How to change fontFamily of TextView in Android
What is the difference between fontFamily and typeFace in android?
it's up to you and depends on your application.. also you can set a background using an xml file
eg:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp" />
<solid android:color="#8c0000" />
</shape>
</item>
<item >
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp" />
<solid android:color="#c62f2c" />
</shape>
</item>
</selector>
In my AndroidManifest file i do not declare a theme.
The result is:
black background and ABS with blue background, also states of list item's is blue.
thats fine.
now i want to make to set the indeterminateProgressStyle to Widget.ProgressBar.Small
Therefore i have to declare my own style like this:
<style name="Custom" parent="??">
<item name="android:actionBarStyle">#style/ActionBarIPS</item>
</style>
what should i enter in the parent parameter?
i want all style behaviors like before (black background with blue ABS and blue list item states etc as it is defined when i dont declare a theme attribute in AndroidManifest.
EDIT:
i also need to know this parent's value:
<style name="ActionBarIPS" parent="ABS with blue background">
<item name="android:indeterminateProgressStyle">#style/IndeterminateProgress</item>
</style>
the version without a style in manifest:
the version with custom style and parent=Theme.Sherlock
i want the first version with indeterminate spinner set to "small"
It's depend to your current style, It can be Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.ForceOverflow and etc, e.g:
<style name="Custom" parent="Theme.Sherlock or Theme.Sherlock.Light">
<item name="android:actionBarStyle">#style/ActionBarIPS</item>
<item name="android:indeterminateProgressStyle">#style/IndeterminateProgress</item>
</style>
Note: You must declare this style in style.xml in your values directory.
Edited:
You got blue ActionBar without using ABS because you're using Samsung TouchWiz default UI.
If you install your APK in non-samsung devices you won't see this blue action bar, But If you are forced to have blue actionbar then put the following image in your drawable directory and set it as your actionbar background through:
getSupportActionBar().setBackgroundDrawable(getResources()
.getDrawable(R.drawable.TouchWiz_ActionBar_Bg));
Try to use "Theme.Sherlock" as a parent. Also I suggest to add:
<item name="actionBarStyle">#style/ActionBarIPS</item>
Can someone tell me the corresponding color code of a selected list item in Holo? Also, what are the colors to generate a long press list item transition in Holo? I know the Holo colors are defined in colors.xml but I'm unsure which ones are used for the selection and long press list item transition. Thanks in advance.
If you have a look at Android's platform framework base, especially the themes.xml file, you will find the answer to your question:
https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/themes.xml
This file defines, amongst others, the following two Holo themes and its corresponding items:
Theme.Holo:
<style name="Theme.Holo">
...
<item name="colorPressedHighlight">#color/holo_blue_light</item>
<item name="colorLongPressedHighlight">#color/holo_blue_bright</item>
...
</style>
Theme.Holo.Light:
<style name="Theme.Holo.Light" parent="Theme.Light">
...
<item name="colorPressedHighlight">#color/holo_blue_light</item>
<item name="colorLongPressedHighlight">#color/holo_blue_bright</item>
...
</style>
I think these are the two colours you are looking for.
The colours are translated into the following hex codes:
<!-- A light Holo shade of blue -->
<color name="holo_blue_light">#ff33b5e5</color>
<!-- A really bright Holo shade of blue -->
<color name="holo_blue_bright">#ff00ddff</color>
You could try this :
android:background="?android:attr/selectableItemBackground"
You could also refer to
How do you get the selection color in Android?
and
Default selector background in Clickable Views
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.
I was changing the background of the Actionbar in my App and to make text readable again I have to change the color of the text. I was successful with title/subtitle and the tab-texts, but I am struggling with the text of the action-items - they stay white no matter what I tried . Anyone has a hint on how to do that?
Also the overflow-icon is white which does not look too good on the wooden background - is that an extremely good reason to use the stuff below? I am not really sure what's the reason to not do it, but as I do not have a big device-test-park I better want to be sure ;-)
<!-- the following can be used to style the overflow menu button
only do this if you have an *extremely* good reason to!! -->
<!--<style name="MyOverflowButton" parent="Widget.Sherlock.ActionButton.Overflow">
<item name="android:src">#drawable/ic_menu_view</item>
<item name="android:background">#drawable/action_button_background</item>
</style>-->
I also struggled with this, but the solution to changing action item text color was:
<style name="My.Theme" parent="Theme.Sherlock.Light">
<item name="android:actionMenuTextColor">#android:color/white</item>
</style>
The key here was to use this attribute in the general theme, not in the actionbar style.
Try starting with android:theme="#style/Theme.Sherlock.Light" in your Manifest and then changing your styles. The light theme features dark action items. Or just look through the light theme to find out how to change them.