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.
Related
UPDATE: As pointed out in the comments, 'Widget.AppCompat.EditText' is the correct parent style for an AppCompatEditText subclass. In my case, the real issue was I had forgotten to assign a value to our control's default style attribute in our theme, so our control wasn't getting any style to use as a default.
However, this question still could use an answer as to how one properly identifies which style to use as a parent when defining your own default styles when subclassing the standard controls. As such, I've also renamed its title.
As such, I'm leaving this open in hopes someone can answer that question since it will help any who wish to do something similar.
We're trying to define a common look-and-feel for all AppCompatEditText controls used throughout the app. As such, rather than having to manually apply the 'style' attribute on each usage, we're instead trying to replace the default style with our own.
Replacing the default style is actually the easy part. What isn't is knowing what the parent style for our style should be set to so we still have all aspects of the original style which we haven't explicitly overwritten with those in ours.
Digging in the source code for AppCompatEditText, it shows the default style to be stored in R.attr.editTextStyle but I'm not sure where now to look to see what value is stored in it.
Experimenting too didn't get us anywhere. No matter what we have tried so far, we lose the default appearance completely. No underline, no background, no padding, nothing. Just the values we've set, which means it's not picking up the parent style.
We've tried the following without success...
<style name="ZinEditText" parent="android:Widget.EditText">
<item name="zinTypeface">light</item>
<item name="android:textSize">#dimen/defaultTextSize</item>
<item name="android:lineSpacingMultiplier">#dimen/defaultLineSpacing</item>
</style>
<style name="ZinEditText" parent="Widget.AppCompat.EditText">
<item name="zinTypeface">light</item>
<item name="android:textSize">#dimen/defaultTextSize</item>
<item name="android:lineSpacingMultiplier">#dimen/defaultLineSpacing</item>
</style>
<style name="ZinEditText" parent="Base.V7.Widget.AppCompat.EditText">
<item name="zinTypeface">light</item>
<item name="android:textSize">#dimen/defaultTextSize</item>
<item name="android:lineSpacingMultiplier">#dimen/defaultLineSpacing</item>
</style>
<style name="ZinEditText" parent="Widget.Holo.EditText">
<item name="zinTypeface">light</item>
<item name="android:textSize">#dimen/defaultTextSize</item>
<item name="android:lineSpacingMultiplier">#dimen/defaultLineSpacing</item>
</style>
As I said, none of the above seemed to work.
So how does one find the actual parent style to use?
To address the immediate issue, the default style for an AppCompatEditText is Widget.AppCompat.EditText. The second example you've shown is the correct one:
<style name="ZinEditText" parent="Widget.AppCompat.EditText">
...
This needs to be set as the editTextStyle in your app theme.
<style name="AppTheme" parent="#style/Theme.AppCompat">
<item name="editTextStyle">#style/ZinEditText</item>
...
Finding these default styles and attributes is not well documented anywhere officially, as far as I'm aware. The official documentation for Styles and Themes simply directs one to the various R.attr pages for the framework and support packages, to "discover" what's available. However, a generally reliable way to find this for most Views that allow a default style is to inspect the source code.
A View subclass will often implement at least three constructors: one that takes only a Context; one that takes a Context and an AttributeSet; and one that takes a Context, an AttributeSet, and an int for defStyleAttr, a default style attribute. This attribute is what we're looking for. It will usually have a sensible name, like editTextStyle, textViewStyle, checkboxStyle, etc. If you already know the name, you can skip checking the View class for it.
In Views that chain their constructors, this attribute will be normally be in the call to the three-parameter constructor from the two-parameter one. In AppCompatEditText, we can see that the name of this attribute is editTextStyle.
public AppCompatEditText(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.editTextStyle);
}
After we've got the name for the attribute, we then head to the res/values/ directory for the platform or support package the View is in. The default value will be in the relevant themes*.xml file for your app's parent theme.
For the platform themes, there is a base themes.xml, and a few others for specific theme versions, such as Holo and Material.
For support library Views, these theme files will be under the package-specific res/values/ directory, and the default attribute value may be in themes.xml or themes_base.xml.
In v7 appcompat, our app's exact parent theme is likely in v7/appcompat/res/values/themes.xml, though most of the themes there are just direct aliases for base themes; i.e., they don't override any of their parents' attribute values. The default for AppCompatEditText is actually in v7/appcompat/res/values/themes_base.xml. There are separate entries for different themes - the regular, and the light - but they are both the same.
<item name="editTextStyle">#style/Widget.AppCompat.EditText</item>
This is enough to determine which style to use as our parent, but should we want to check out the style specifics, we can then refer to v7/appcompat/res/values/styles.xml, where we find that style's parent:
<style name="Widget.AppCompat.EditText" parent="Base.Widget.AppCompat.EditText"/>
which leads us to v7/appcompat/res/values/styles_base.xml:
<style name="Base.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText" />
<style name="Base.V7.Widget.AppCompat.EditText" parent="android:Widget.EditText">
<item name="android:background">?attr/editTextBackground</item>
<item name="android:textColor">?attr/editTextColor</item>
<item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
</style>
I'd like to override the accentColor used by v14 PreferenceFragmentCompat.
I'm using a pink accent color for the outer frame of my Android app. This creates problems in many situations as it causes standard controls to use an accent color that's close enough to red that the effect is disturbing. Be that as it may, I like the effect of having a pink FAB and button controls on the frame.
For child activities, I use a them with the standard teal accent color. However, I have a PreferenceFragment compat in a drawer on the main activity, and I cannot figure out how to override the main activity's accent color.
Things I have tried (none of which work):
Setting a theme on the frame of the fragment that receives the PreferenceFragmentCompat (doesn't work):
<FrameLayout android:id="#+id/preferenceFragmentFrame"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1"
android:theme="#style/AppTheme.TealAccentColor"
/>
where the AppTheme.TealAccentColor style provides an explicit teal acccentColor.
Setting the accentColor in a preference theme (doesn't work):
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="preferenceTheme">#style/MyPreferenceThemeOverlay</item>
</style>
<style name=MyPreferenceThemeOverlay parent="PreferenceThemeOverlay.v14.Material>
<item name="colorAccent">#color/colorAccentTeal</item>
</style>
Adding an accent color to preference-v14's PreferenceThemeOverlay (doens't work):
<!-- use the library's theme-->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="preferenceTheme">PreferenceThemeOverlay.v14.Material</item>
</style>
<!-- but add an accentColor item to the library's theme -->
<style name="PreferenceThemeOverlay.v14">
<item name="colorAccent">#color/colorAccentTeal</item>
</style>
No matter what I do, PreferenceFragmentCompat seems to take the pink accent color from the Activity's theme instead.
I'm sure it has something to do with a disconnect between the Activity's theme and a Fragment's theme. But there's no xml element for the fragment, since PreferenceFragmentCompat provides its own layout.
Maybe there's a way to do it programmatically with an override in the class that extends PreferenceFragmentCompat, but if there is, I can't imagine what it would be. Most of the attack points I can think of either have access to the internally-created layout, or have access to the layout after it has been created, which is too late.
A picture might help:
Have you tried overriding android:colorAccent instead of colorAccent?
Use the method you described as "Setting the accentColor in a preference theme", just change the attribute name.
The preference support library doesn't take into consideration appcompat at all, so
forget about appcompat attributes
color* attributes will only work on API 21, I think
If you want consistent behavior you can use my library Android Support Preference which aims to connect preference and appcompat support libraries.
Then you original style with colorAccent (unprefixed) will work as expected.
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);
A create a simple Theme as
<style name='one'>
<item name='android:textColor'>#eea</item>
<item name='android:textSize'>20sp</item>
</style>
However on viewing in the emulator the screen goes black.when i do not apply theme the screen has a white background .
what really happens here.i am just starting with android.
In addition ,if a apply a theme to my activity then the attributes of the theme applies to all components of my activity say button,textfields and edittexts .
why would i then write
android:textSize=?android:textSize
to reference value from the theme for any button in my layout when the same value would already be applying.
is the syntax above the correct way to reference an attribute from my theme to assign to attribute for any view in my layout.
thanks
tejinder
Yeah, so you need to do a little more reading.
Let's start with the basics,
You need to understand the differente betweent an Attribute, a Style, and a Theme.
An Attribute is something that can be styled. For instance: android:textSize is an attribute that can have any value.
A Style is a set of specific attributes that will be applied to a Widget. They are defined
in your /values/styles.xml
For instance:
<style name="normalTextThin" parent="android:Widget.Holo.Light.TextView">
<item name="android:gravity">left|center_vertical</item>
<item name="android:padding">8dp</item>
<item name="android:textColor">#FFFFFF</item>
<item name="android:textSize">16sp</item>
</style>
The styles can be applied either as part of a theme or directly as theme-independent.
Theme-indepentent styling of a widget is like this:
<TextView
android:id="#+id/text"
style="#style/normalTextThin"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
You are then theming only that one TextView.
A Theme is a collection of Styles that can be applied to a part of your UI, such a a whole Activity, or your whole Application.
For instance:
<style name="AppTheme" parent="android:Theme.Light">
<item name="android:editTextStyle">#style/EditTextAppTheme</item>
<item name="android:buttonStyle">#style/ButtonAppTheme</item>
<item name="android:imageButtonStyle">#style/ImageButtonAppTheme</item>
</style>
Here, we are declaring that all EditText in your application will use the style named EditTextAppTheme, and so forth and on. When done like this, in order to actually have the theme be active, you declare it in the manifest:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
That means that you are not required to declare the style on each widget you create.
<EditText
android:id="#+id/input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="#string/hint_search">
<requestFocus />
</EditText>
That widget right there would already be styled using EditTextAppTheme without the need of you explicitely declaring so.
I recommend you try to read on what attributes can be styled, how to style them, and so forth and on.
If you don't want to though, it's fine, you can still get a lot done with the following tools for styling:
ActionBarStyleGenerator to help you create styles for the ActionBar.
Android Holo Colors to help you style standard widgets.
Hope that helps.
Additional Info
Let me clarify on the whole ?attr/attributeName
The ? means that the system will choose the specific attributeName value for the current Configuration (not specific to different themes). This should be used only when you want the value to be different on different configurations. For example:
?android:attr/actionBarSize
This line is a dimension, and it will be different not based on the current theme, but on the current device screen size and orientation (values, values-land, values-sw600dp).
It's important to know that specifying ?android: means you are accessing preset Android values, not yours. If you have or want to create and use your own attribute values for specific configurations, you must do the following:
Create a file named attrs.xml on your /values/ folder.
Declare the desired custom attribute:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<attr name="my_custom_attr" format="reference" />
</resources>
Declare a value for the custom attribute, let's say on your own theme.
<style name="AppTheme" parent="android:Theme.Light">
<item name="my_custom_attr">#resource_type/resource_name</item>
<item name="android:editTextStyle">#style/EditTextAppTheme</item>
<item name="android:buttonStyle">#style/ButtonAppTheme</item>
<item name="android:imageButtonStyle">#style/ImageButtonAppTheme</item>
</style>
And then you can use it on the Widget you'd like:
Hope that clears things out.
EDIT 2
For a better answer to your question, please update your question. And like I said, read more on how to properly create styles.
The Theme named 'one', what do you want to apply it to? An activity, a Widget, the whole Application?
How are you applying the theme? Show the lines of code where you specify the usage of theme 'one'.
Your theme as you specified is simply not a properly constructed theme/style.
<style name='one'>
<item name='android:textColor'>#eea</item>
<item name='android:textSize'>20sp</item>
</style>
This says absolutely nothing, and it is definitely not suitable for an Activity-level theme. The reason you specify a parent is so your theme can inherit all of the attributes from the parent, and then you specifiy which ones to change.
For instance, if you want to use your theme and have a light background, do this:
<style name='one' parent="android:Theme.Holo.Light>
<item name='android:textColor'>#eea</item>
<item name='android:textSize'>20sp</item>
</style>
But even here, despite the fact that it will apply, you don't want to have the same text color and size for the whole application do you? That'd be nonsense, different text color and sizes account for a big part of the user experience, so rather than setting those values from what we can refer to as the main style, we can create substyles and apply them to certain widgets.
I can't really go any more detailed that what I already have, the above explains how to accomplish Widget-specific styling, and activity/application level theming.
For a complete start-up guide, read the Android Developer Site, try the test styles declared there, see how they work, and until then try to create your own, don't try to create something out of nowhere if no reading has been made.
I've got an application that draws a custom title bar using the following style for the application theme:
<style name="App_Theme" parent="android:Theme">
<item name="android:windowTitleSize">30dip</item>
<item name="android:windowTitleBackgroundStyle">#style/App_TitleBackground</item>
</style>
This does not give me the holo theme. So I set the to parent="#android:style/Theme.Holo". This crashes the application with the following error:
E/AndroidRuntime(2048): Caused by: android.util.AndroidRuntimeException: You cannot combine custom titles with other title features
Is it disallowed to use custom title bar using:
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
Or am I missing something here?
PS:
The code works perfectly fine with the parent set to "Android:Theme".
I'm using API Level 14
Let your activity use a theme with following attribute:
<item name="android:windowActionBar">false</item>
mido is correct
<item name="android:windowActionBar">false</item>
is the solution when using #android:style/Theme.Holo (default for ICS).
One observation, if you use #android:style/Theme.Holo.NoActionBar change it back to the default #android:style/Theme.Holo.
Mostly for the benefit of future visitors, one cannot use Window.FEATURE_CUSTOM_TITLE with Holo themes. Setting android:windowActionBar is not what I wanted, since I wanted a title bar, although a customized one.
My workaround, was to requestWindowFeature(Window.FEATURE_NO_TITLE) and add another layout that looked like a titlebar in my activity. That way, I get the best of both worlds.