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
Related
Currently I am having four themes in my styles.xml file. (theme1, theme2, theme3, theme4).
I am having a test_image.xml in both drawable & drawable-theme1 folders.
What I need is, for theme1 alone, I need resources from drawable-theme1, and for other themes (theme2, theme3, theme4), I need resources from normal drawable folder.
Finally, while setting the image view programmatically, I will be calling imageView.setResource(R.drawable.test_image). The resource file must be retrieved based on my selected theme.
Whether this is possible?
I could not get any questions or similar relevant to this. If anyone has provided a solution already, please help me finding that. Thanks
You can't use <themeName> as a qualifier.
It means that you can't use a drawable-themeName folder.
However you can define a custom attribute in the attrs.xml file:
<resources>
<attr name="myDrawable" format="reference" />
</resources>
Then in styles.xml in the app theme you can define it:
<style name="AppTheme1" parent="Theme.MaterialComponents.DayNight">
<!-- ..... -->
<item name="myDrawable">#drawable/ic_add_24px</item>
</style>
Finally in a layout you can use something like:
<com.google.android.material.button.MaterialButton
....
app:icon="?attr/myDrawable"/>
or programmatically:
MaterialButton button = findViewById(R.id.button);
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.myDrawable, typedValue, true);
button.setIcon(ContextCompat.getDrawable(this,typedValue.resourceId));
I know it is possible to create custom UI element (by way of View or specific UI element extension). But is it possible to define new properties or attributes to newly created UI elements (I mean not inherited, but brand new to define some specific behavior I am not able to handle with default propertis or attributes)
e.g. element my custom element:
<com.tryout.myCustomElement
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Element..."
android:myCustomValue=<someValue>
/>
So is it possible to define MyCustomValue?
Thx
Yes. Short guide:
Create an attribute XML
Create a new XML file inside /res/values/attrs.xml, with the attribute and its type
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="MyCustomElement">
<attr name="distanceExample" format="dimension"/>
</declare-styleable>
</resources>
Basically you have to set up one <declare-styleable /> for your view that contains all your custom attributes (here just one). I never found a full list of possible types, so you need to look at the source for one I guess. Types that I know are reference (to another resource), color, boolean, dimension, float, integer and string. They are pretty self-explanatory
Use the attributes in your layout
That works the same way you did above, with one exception. Your custom attribute needs its own XML namespace.
<com.example.yourpackage.MyCustomElement
xmlns:customNS="http://schemas.android.com/apk/res/com.example.yourpackage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Element..."
customNS:distanceExample="12dp"
/>
Pretty straight forward.
Make use of the values you get passed
Modify the constructor of your custom view to parse the values.
public MyCustomElement(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomElement, 0, 0);
try {
distanceExample = ta.getDimension(R.styleable.MyCustomElement_distanceExample, 100.0f);
} finally {
ta.recycle();
}
// ...
}
distanceExample is a private member variable in this example. TypedArray has lots of other things to parse other types of values.
And that's it. Use the parsed value in your View to modify it, e.g. use it in onDraw() to change the look accordingly.
In your res/values folder create attr.xml. There you can define your attribues:
<declare-styleable name="">
<attr name="myCustomValue" format="integer/boolean/whatever" />
</declare-styleable>
When you then want to use it in your layout file you have to add
xmlns:customname="http://schemas.android.com/apk/res/your.package.name"
and then you can use the value with customname:myCustomValue=""
Yes , you can.Just use <resource> tag.
like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CodeFont" parent="#android:style/TextAppearance.Medium">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#00FF00</item>
<item name="android:typeface">monospace</item>
</style>
</resources>
link from official website
Background
I'm trying to set a custom margin value on the listView items based on the selected theme.
The app has multiple themes, and the user can choose which theme to use , which I set by calling "setTheme()" .
The problem
whatever I try, I get this error:
java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x2
note that this occurs only for the margin attribute, and so far no other attribute has caused this.
What I've tried
first, here's the xml snippets I've used
attrs.xml
<attr name="listview_item__horizontal_spacing" format="dimension" />
styles.xml
<style name="AppTheme_HoloDark" parent="#style/Theme.Sherlock">
<item name="listview_item__horizontal_spacing">4dp</item>
....
the layout of the listView item:
<RelativeLayout ...
android:layout_marginLeft="?attr/listview_item__horizontal_spacing" >
I've also tried using "reference" for the attribute type, and reference to a "dimen" resource, but it also cause the same exception.
Another thing I've tried is getting it dynamically:
public static int getResIdFromAttribute(final Activity activity,final int attr)
{
final TypedValue typedvalueattr=new TypedValue();
activity.getTheme().resolveAttribute(attr,typedvalueattr,true);
return typedvalueattr.resourceId;
}
...
final int spacingResId=getResIdFromAttribute(activity,R.attr.listview_item__horizontal_spacing);
but for some reason I get 0 as the result of this call. Only when using this method it worked.
The question
What is going on? How can I avoid this?
Is there really no way to overcome this but using code (when inflating the xml) ?
You don't need to use attr XML, and this is your issue.
Place your the value you want in a file named dimens.xml in your res folder (same location as strings.xml).
This file will look something like this:
<resources>
<dimen name="value1">15dp</dimen>
<dimen name="value2">20dp</dimen>
</resources>
Then in your layout XML, you can reference the dimen directly (just as you would reference a string), something like this:
<RelativeLayout ...
android:layout_marginLeft="#dimen/value1" >
or when defining a style, in your XML it would look like:
<style name="MyStyle1">
<item name="android:layout_marginLeft">#dimen/value1</item>
</style>
and then for your other style:
<style name="MyStyle2">
<item name="android:layout_marginLeft">#dimen/value2</item>
</style>
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});
in attrs I have
<attr name="bzz" format="color" />
then in theme
<style name="mytheme" parent="android:Theme">
<item name="bzz">#color/aaa</item>
</style>
and in the code
this works great
tv.setBackgroundResource(R.color.aaa);
but when I do this it gives me an error
tv.setBackgroundResource(R.attr.bzz);
I do not understand what is the problem, my logic is that I set the bzz as reference to color so that should work fine, but it does not :)
it says like android.content.res.Resources$NotFoundException: Resource ID #0x7f010008
but I do not understand what resource can't be found ?
I am sure that the color is there sins if I set it directly it works great, what exacly is the thing that is not linked correctly
Thanks
You need to resolve the attr to get the corresponding color's resource id. Then you can set the TextView's background resource to the obtained resource id.
Example code:
TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.bzz, typedValue, true);
tv.setBackgroundResource(typedValue.resourceId);