Custom Android attribute without namespace - android

I'm making custom LayoutInflater.Factory and in onCreateView() method I'd like to obtain custom attribute specified for current view. However I don't want to declare styleable for this attribute. I can get this parameter if it was specified in xml attribute for this View using:
attrs.getAttributeValue(null, attributeName);
I can even get it if it was specified in style parameter of the View using:
context.obtainStyledAttributes(attrs, new int[]{R.attr.custom_attribute);
However it seems to be impossible to get this attribute if it's specified in theme. Any help would be appreciated.
Here is my xml resouces:
<style name="CustomTheme"
parent="android:Theme.DeviceDefault">
<item name="android:textViewStyle">#style/TextView.Custom</item>
</style>
<style name="TextView.Custom">
<item name="custom_attribute">"blabla"</item>
</style>
P.S. I know I should declare slyleable resource for custom attributes, and it works if I do so, but I want to be sure it is not possible otherwise because it is simplier to use this way.

Turnd out you can get it usig this:
TypedValue value = new TypedValue();
theme.resolveAttribute(android.R.attr.textViewStyle, value, true);
typedArray = theme.obtainStyledAttributes(value.resourceId, new int[] {R.attr.custom_attribute});

Related

How to get an ?attr/ value programmatically

I'm trying to do some custom view styling and I'm having trouble correctly picking up styled attributes from the theme.
For instance, I would like to get the themes EditText's Text Colour.
Looking through the theme stack you can see my theme uses this to style it's EditText's:
<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>
What I'm looking for, is how do I get that ?attr/editTextColor
(Aka, the value assigned by the theme to "android:editTextColor")
Searching through google, I have found enough of this answer:
TypedArray a = mView.getContext().getTheme().obtainStyledAttributes(R.style.editTextStyle, new int[] {R.attr.editTextColor});
int color = a.getResourceId(0, 0);
a.recycle();
But I'm pretty sure I must be doing this wrong as it always displays as black rather than grey?
As commented from #pskink:
TypedValue value = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.editTextColor, value, true);
getView().setBackgroundColor(value.data);
will pull the attribute from the theme currently assigned to the context.
Thanks #pskink!
Have you tried this ?
EDIT :
This is my short answer if you want a complete answer ask me
Your attrs.xml file :
<resources>
<declare-styleable name="yourAttrs">
<attr name="yourBestColor" format="color"/>
</declare-styleable>
</resources>
EDIT 2 : Sorry I forgot to show how I'm using my attr value in layout.xml, so :
<com.custom.coolEditext
android:id="#+id/superEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:yourBestColor="#color/any_color"/>
and then in your custom Editext :
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.yourAttrs, 0, 0);
try {
int colorResource = a.getColor(R.styleable.yourAttrs_yourBestColor, /*default color*/ 0);
} finally {
a.recycle();
}
I'm not sure if it is the answer what you want but it can put you on good way

Default Style Resource pre API Level 21

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.

How to get the parent theme programmatically

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.

Syntax for Retrieving Value from Nested Style Resource?

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>

Set style for TextView programmatically

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.

Categories

Resources