Error when getting a dimension in theme for margins - android

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>

Related

How to use a different drawable res folder (for eg. drawable-theme1) when the appTheme is selected as "theme1" from styles.xml? Is this possible?

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));

Android how to use app attribute for custom class options [duplicate]

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

How to set text size for app in one place?

I want to be able to set the text size on an application wide basis.
So I will need to be able to access the value in code and xml.
I tried putting the size in my dimens.xml as follows...
<item name="text_size_small" type="dimen" format="float">15.0</item>
But I was unable to re-use this in xml i.e...
android:textSize="#dimen/text_size"
Gives an exception...
android.view.InflateException: Binary XML file line #29: Error inflating class RadioButton
Also I think I would need to specify the units i.e 'sp' in this case.
So is there a way to do it?
If not, i will probably have to set all text size's that need setting, programmatically.
Hopefully someone knows a better way.
inside dimens.xml, you can use the <dimen item, and specify the unit:
<dimen name="text_size_small">15sp</dimen>
to access it programmatically, you
textview.setTextSize(getResources().getDimension(R.dimen.text_size_small), TypedValue.COMPLEX_UNIT_PX);
in dimens the size is already scaled for the unit, so you don't need to apply the conversion again, hence the TypedValue.COMPLEX_UNIT_PX
In your dimens.xml specify your like this
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="text_size_small">15sp</dimen>
</resources>
In your layout use this
android:textSize="#dimen/text_size_small"
If you want to set this programtically, try like this
textview.setTextSize(getResources().getDimension(R.dimen.text_size_small));
You must define own textview style.
#styles
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="android:textViewStyle">#style/CMTextView</item
</style>
<style name="CMTextView" parent="android:style/Widget.TextView">
<item name="android:textSize">#dimen/text_size</item>
</style>
#dimens
<dimen name="text_size">18sp</dimen>

Action bar size in custom xml file

I'm not able to find a way to insert the action bar size attribute in my own xml file. Since I'm using Google maps in my activity, I can't use the built-in xml attribute at all in my layout, but I have to call setPadding() on the map using the action bar size. Now, I found the way to retrieve the actionBarSize at runtime, but I wonder if I could skip this code completly, inserting the android attribute in my onw xml, for example:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<dimen name="myownsize">#android:attr/actionBarSize</dimen>
</resources>
But in this way it doesn't work. Is there a way to do it?
Define a attr in your own project. Create res/values/attrs.xml and add this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myownsize" format="dimension"></attr>
</resources>
In your res/values/styles.xml, under your parent theme definition, initialize this attribute:
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="myownsize">?android:attr/actionBarSize</item>
</style>
Once this is done, you can use ?attr/myownsize as a dimension:
android:padding="?attr/myownsize"
Sorry if I too, misunderstood the question.

How to use a theme defined variable in res/values/dimens.xml?

Howdy.
In my themes.xml definition, I have the following:
<style name="mythemename">
<item name="d_myvar">100dip</item>
</style>
I would like to be able to reference this in res/values/dimens.xml like so:
<dimen name="myvar">?d_myvar</dimen>
Alas, this doesn't work. When I try to use the #dimen/myvar as the height of a LinearLayout, the app crashes with the error "You must supply a layout height attribute."
I have also tried
<dimen name="myvar" value="?d_myvar" />
But that won't compile.
How can I define #dimen/myvar in my xml so that it loads the ?d_myvar variable defined in the theme?
Thanks!
I saw your help request on the Italian Startup Scene.
Unfortunately, according to the syntax of the dimen tag:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen
name="dimension_name"
>dimension</dimen>
</resources>
you simply can't do it. In fact, you can reference theme attributes when the syntax specifies:
?[package:][type:]name
Solutions:
Gix's answer would be the standard way of defining and reusing dimensions.
Maybe you can reorganize your code to reuse d_myvar through inheritance?
As a last and desperate resort, I would go for a shell script that automates the process of variable substitution using xml command line tools. I have never personally used them, but see for example xmllint, xmlstarlet or this article.
Put the 100dip part in your dimens.xml like so:
<dimen name="myvar">100dp</dimen>
then in your theme you can reference it as #dimen/myvar if you need, or you can reference it in code using R.dimen.myvar
In other words, you don't set the dimension in theme and then reference it in dimens.xml, but you go the other way around. You set the dimension in dimens.xml and then reference that in your theme/style xml.
In styles.xml
<resources>
<attr format="dimension" name="exampleDimension"/>
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="exampleDimension">100dp</item>
...
</style>
</resources>
Then in your other xml to use the new attribute, you would use it like this
android:padding="?attr/exampleDimension"

Categories

Resources