My app has several themes from which the user can chose (red, blue, green etc.) Every theme has a corresponding button style (RedTheme => RedButton).
Now I have created a custom view which slightly extends "Button" and unfortunately this button does not get any style, except I use the "style" attribute in a layout.xml.
I don’t use any additional attributes, I just want that my Button style is applied.
I want to do something similar than:
<item name="android:imageButtonStyle">#style/ImageButtonmyTimeRed</item>
I have already read many other posts like this one:
How to: Define theme (style) item for custom widget
but I’m not 100% sure if this solution fits for me since I don’t need any additional attributes.
I'm using the three parameter super constructor and pass the button style resource id. This works for all my custom themes. Thanks #pskink for pointing me into right direction.
public ClockButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.buttonStyle);
...
}
My theme looks like this:
<style name="LilaTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="buttonStyle">#style/ButtonmyTimeLila</item>
</style>
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 am looking to create a custom ViewGroup to be used in a library; which contains a few ImageButton objects. I would like to be able to apply a style each ImageButton; but I cannot figure out how to apply a style programmatically other than by applying a attribute resource to the defStyleAttr parameter; like so:
mImageButton = new ImageButton(
getContext(), // context
null, // attrs
R.attr.customImageButtonStyle); // defStyleAttr
The issue with this is that the only way to change the style of each ImageButton would be by applying a style to this attribute in a parent theme. But I would like to be able to set a default style, without having to manually set this attribute for each project that uses this library.
There is a parameter that does exactly what I am looking for; defStyleRes, which can be used like so:
mImageButton = new ImageButton(
getContext(), // context
null, // attrs
R.attr.customImageButtonStyle, // defStyleAttr
R.style.customImageButtonStyle); // defStyleRes
This parameter is only available at API Level 21 and above, but my projects target API Level 16 and above. So how can I set the defStyleRes, or apply a default style, without access to this parameter?
I applied my style using a ContextThemeWrapper, as suggested by #EugenPechanec, which seems to work well, but each ImageButton now has the default ImageButton background, even though my style applies <item name="android:background">#null</item>.
Here is the style I am using:
<style name="Widget.Custom.Icon" parent="android:Widget">
<item name="android:background">#null</item>
<item name="android:minWidth">56dp</item>
<item name="android:minHeight">48dp</item>
<item name="android:tint">#color/selector_light</item>
</style>
And this is how I am applying it:
ContextThemeWrapper wrapper = new ContextThemeWrapper(getContext(), R.style.Widget_Custom_Icon);
mImageButton = new AppCompatImageButton(wrapper);
On the left is what I am getting, and on the right is what I would like it to look like:
defStyleAttr is for resolving default widget style from theme attribute.
Example: AppCompatCheckBox asks for R.attr.checkBoxStyle. Your theme defines <item name="checkBoxStyle">#style/Widget.AppCompat.CheckBox</item>.
If that attribute is not defined in your theme the widget would pickup its defStyleRes e.g. R.style.Widget_AppCompat_CheckBox.
Note that these are not actual values used by the widget.
I have not seen defStyleRes constructor parameter used outside of the framework. All of these parameters (plus defaults) are however used when asking TypedArray for resources.
How to actually solve your problem
So the four parameter constructor is not available on all platforms. You need to find a way to feed in your default style. Consider a style you'd like to apply:
<style name="MyImageButtonStyle" parent=""> ... </style>
You need a way to convert it to a defStyleAttr parameter. Define the default style on a theme overlay:
<style name="MyImageButtonThemeOverlay" parent="">
<!-- AppCompat widgets don't use the android: prefix. -->
<item name="imageButtonStyle">#style/MyImageButtonStyle</item>
</style>
Now you can create your ImageButton using this theme overlay:
// When creating manually you have to include the AppCompat prefix.
mImageButton = new AppCompatImageButton(
new ContextThemeWrapper(getContext(), R.style.MyImageButtonThemeOverlay)
);
You don't need to specify any other parameters as AppCompatImageButton will pickup R.attr.imageButtonStyle by default.
If that looks hacky you can always inflate your custom view hierarchy or individual widgets from XML where you specified the style="#style/MyImageButtonStyle" attribute.
I'm having a slight issue with the Light theme and a fast scrolling ListView. As you can see in the image below, when using the Light theme the pop up for the current letter when dragging the scroll bar contains black text on a dark gray background, making it very hard to read and just look ugly.
If I override android:textColorPrimary in a Theme it changes color, but so do all the items in the ListView, as well as other UI elements elsewhere in the app, so it's not a viable solution.
I'm using GreenDroid and it's associated classes, but looking through the source code, it seems to just use a standard ListView so is unlikely to be the issue.
I also found this question, which makes it seem likely that it's not a GreenDroid issue, but unfortunately the solution given by the author in a comment doesn't explain in enough detail how to fix it.
Any ideas as to how to solve this?
Thanks,
Daniel
The easiest way to do this is to define a special Theme that you apply only to the ListActivity, like this.
<style name="Theme.MyApp" parent="#style/Theme.GreenDroid.Light.NoTitleBar">
...
</style>
<style name="Theme.MyApp.TweakedItems" parent="#style/Theme.MyApp">
...
<!-- Default color for Android Dark Theme -->
<item name="android:textColorPrimary">#android:color/primary_text_dark</item>
</style>
Then you can modify any ItemView from GreenDroid to apply a different color.
<?xml version="1.0" encoding="utf-8"?>
<greendroid.widget.itemview.TextItemView
xmlns:android="http://schemas.android.com/apk/res/android"
style="?attr/gdTextItemViewStyle"
android:id="#+id/gd_text"
android:layout_height="?attr/gdItemViewPreferredHeight"
android:minHeight="?attr/gdItemViewPreferredHeight"
android:paddingLeft="?attr/gdItemViewPreferredPaddingLeft"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center_vertical"
android:textColor="#000000" />
Starting from API Level 11 you can change the color using fastScrollTextColor
i've found an answer here:
https://gist.github.com/DHuckaby/d6b1d9c8e7f9d70c39de
public class CustomListView extends ListView {
public CustomListView(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, R.style.CustomListView), attrs);
}
}
styles.xml
<resources>
<style name="CustomListView" parent="#style/GlobalTheme">
<item name="android:textColorPrimary">?android:textColorPrimaryInverse</item>
</style>
</resources>
Is there a way to format all TextViews, Buttons or whatever with a theme ?
Like in CSS when i want to format all a-tags
a{
/some css here/
}
I want to do that in android via xml when I'm applying a theme to my application.
Any ideas ?Thanks
http://bartinger.at/
Update 1.0:
I want to create a theme that formats the text in all TextViews green and in all EditTexts red. So that i just apply the theme and I never have to worry about the style attribute!
Update 1.1:
So I found some that piece of code and I think that's a good beginning
<item name="android:textViewStyle">#style/MyTextView</item>
<item name="android:buttonStyle">#style/MyButton</item>
I think thats the answer to my question. But I have another one. I want to write my own ActionBar and wanted to know how I can apply a default style or default attributes (again without adding the style attribute in the layout xml :P )
I have a class
public class ActionBar extends LinearLayout{ }
and I'm gonna use it like that in my application
<at.bartinger.uil.ActionBar>....</at.bartinger.uil.ActionBar>
The ActionBar should have some default attributes (like height and width) and then adding some custom style attributes which could change from app to app (like background)
yes you can you can apply a theme to the whole application and then all your textviews will have that style.
Inside the styles.xml file you have to define your CustomTheme
for example:
<style name="CustomTheme" parent="android:Theme.Light">
<item name="android:windowBackground">#color/custom_theme_color</item>
<item name="android:colorBackground">#color/custom_theme_color</item>
</style>
you add something like text
"android:textStyle="myStyle" and specify the details in Mystyle
You can apply a style read more here.
for the action abr you should look here http://developer.android.com/guide/topics/ui/actionbar.html
especially at the bottom it explains very well how to style the bar
I'm trying to use the TextView constructor with style like this:
TextView myText = new TextView(MyActivity.this, null, R.style.my_style);
However, when I do this, the text view does not appear to take the style (I verified the style by setting it on a static object).
I've also tried using myText.setTextAppearance(MyActivity.this, R.style.my_style) but it also doesn't work.
I do not believe you can set the style programatically. To get around this you can create a template layout xml file with the style assigned, for example in res/layout create tvtemplate.xml as with the following content:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="This is a template"
style="#style/my_style" />
then inflate this to instantiate your new TextView:
TextView myText = (TextView)getLayoutInflater().inflate(R.layout.tvtemplate, null);
You can create a generic style and re-use it on multiple textviews like the one below:
textView.setTextAppearance(this, R.style.MyTextStyle);
Edit: this refers to the Context object.
You can pass a ContextThemeWrapper to the constructor like this:
TextView myText = new TextView(new ContextThemeWrapper(MyActivity.this, R.style.my_style));
You can set the style in the constructor (but styles can not be dynamically changed/set).
View(Context, AttributeSet, int) (the int is an attribute in the current theme that contains a reference to a style)
Answer from Romain Guy
reference
Parameter int defStyleAttr does not specifies the style. From the Android documentation:
defStyleAttr - An attribute in the current theme that contains a
reference to a style resource that supplies default values for the
view. Can be 0 to not look for defaults.
To setup the style in View constructor we have 2 possible solutions:
With use of ContextThemeWrapper:
ContextThemeWrapper wrappedContext = new ContextThemeWrapper(yourContext, R.style.your_style);
TextView textView = new TextView(wrappedContext, null, 0);
With four-argument constructor (available starting from LOLLIPOP):
TextView textView = new TextView(yourContext, null, 0, R.style.your_style);
Key thing for both solutions - defStyleAttr parameter should be 0 to apply our style to the view.
Dynamically changing styles is not supported (yet). You have to set the style before the view gets created, via XML.
When using custom views that may use style inheritance (or event styleable attributes), you have to modify the second constructor in order not to lose the style. This worked for me, without needing to use setTextAppearence():
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, attrs.getStyleAttribute());
}
The accepted answer was great solution for me. The only thing to add is about inflate() method.
In accepted answer all android:layout_* parameters will not be applied.
The reason is no way to adjust it, cause null was passed as ViewGroup parent.
You can use it like this:
View view = inflater.inflate(R.layout.view, parent, false);
and the parent is the ViewGroup, from where you like to adjust android:layout_*.
In this case, all relative properties will be set.
Hope it'll be useful for someone.
I met the problem too, and I found the way to set style programatically. Maybe you all need it, So I update there.
The third param of View constructor accepts a type of attr in your theme as the source code below:
public TextView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
So you must pass a type of R.attr.** rather than R.style.**
In my codes, I did following steps:
First, customize a customized attr to be used by themes in attr.xml.
<attr name="radio_button_style" format="reference" />
Second, specific your style in your used theme in style.xml.
<style name="AppTheme" parent="android:Theme.Translucent">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<item name="radio_button_style">#style/radioButtonStyle</item>
</style>
<style name="radioButtonStyle" parent="#android:style/Widget.CompoundButton.RadioButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">64dp</item>
<item name="android:background">#000</item>
<item name="android:button">#null</item>
<item name="android:gravity">center</item>
<item name="android:saveEnabled">false</item>
<item name="android:textColor">#drawable/option_text_color</item>
<item name="android:textSize">9sp</item>
</style>
At the end, use it!
RadioButton radioButton = new RadioButton(mContext, null, R.attr.radio_button_style);
the view created programatically will use the specified style in your theme.
You can have a try, and hope it can work for you perfectly.
We can use TextViewCompact.setTextAppearance(textView, R.style.xyz).
Android doc for reference.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
textView.setTextAppearance(R.style.yourStyle)
you can use Extension Functions kotlin
fun TextView.setStyle(#StyleRes resId: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setTextAppearance(resId)
} else {
setTextAppearance(context, resId)
}
}
I have only tested with EditText but you can use the method
public void setBackgroundResource (int resid)
to apply a style defined in an XML file.
Sine this method belongs to View I believe it will work with any UI element.
regards.