Shape Drawable parameters depending on applied style

Let’s say I have a simple shape drawable that draws a ring as follows:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:innerRadiusRatio="3"
    android:shape="ring"
    android:thicknessRatio="36"
    android:useLevel="false" >

    <gradient
        android:centerColor="@android:color/holo_blue_bright"
        android:centerY="0.001"
        android:endColor="@android:color/holo_blue_dark"
        android:gradientRadius="202.5"
        android:startColor="@android:color/transparent"
        android:type="radial"
        android:useLevel="false" />

</shape>

And I apply this shape as a view background as follows:

<View
    android:layout_width="96dp"
    android:layout_height="96dp"
    android:padding="16dp"
    android:background="@drawable/custom_shape" />

The exact details of the shape are not relevant for this question – just suffice to say I have a shape. Now, I don’t want to hard-code the various parameters for the shape. Let’s take thicknessRatio as an example. If i wanted the thickness ratio to change depending on say, screen configuration, I’d of course use an integer resource as follows. I’d have a values.xml with the following:

<item name="thickness_ratio" type="integer" format="integer">56</item>

And then, android:thicknessRatio="@integer/thickness_ratio".

So far, so good. Now, I also want to have two “flavors” of my my shape drawable – a “large” one and a “small” one, and I wish to query the thickness ratio depending not on configuration, but on the style applied to the View. Which is what styles and themes are for. So, here’s what I tried:

Step 1: Declare an attribute for thickness ratio (attrs.xml):

<resources>
        <attr name="thicknessRatio" format="reference" />
</resources>

Step 2: Declare two styles utilizing this attribute (styles,xml):

<style name="CustomShapeSmall">
    <item name="thicknessRatio">@integer/thickness_ratio_small</item>
</style>

<style name="CustomShapeLarge">
    <item name="thicknessRatio">@integer/thickness_ratio_large</item>
</style>

Step 3: Define the two integers in values.xml:

<item name="thickness_ratio_small" type="integer" format="integer">32</item>
<item name="thickness_ratio_large" type="integer" format="integer">56</item>

Step 4: Query the custom attribute in the Shape Drawable:

android:thicknessRatio="?attr/thicknessRatio"

Step 5: Apply the desired style to the View:

<View
    android:layout_width="96dp"
    android:layout_height="96dp"
    android:padding="16dp"
    style="@style/CustomShapeSmall"
    android:background="@drawable/custom_shape" />

Unfortunately, this does not work. I get the following exception:

Caused by: android.content.res.Resources$NotFoundException: File res/drawable/custom_shape.xml from drawable resource ID #0x7f020000
    at android.content.res.Resources.loadDrawable(Resources.java:2091)
    at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
    at android.view.View.<init>(View.java:3364)
    at android.view.View.<init>(View.java:3293)
    ... 28 more
Caused by: java.lang.NumberFormatException: Invalid float: "?2130771969"
    at java.lang.StringToReal.invalidReal(StringToReal.java:63)
    at java.lang.StringToReal.parseFloat(StringToReal.java:310)
    at java.lang.Float.parseFloat(Float.java:300)
    at android.content.res.TypedArray.getFloat(TypedArray.java:287)
    at android.graphics.drawable.GradientDrawable.inflate(GradientDrawable.java:827)
    at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:901)
    at android.graphics.drawable.Drawable.createFromXml(Drawable.java:837)
    at android.content.res.Resources.loadDrawable(Resources.java:2087)

From the exception stack trace, it appears that the line android:thicknessRatio="?attr/thicknessRatio"resolves to a question mark followed by the integer value of R.attr.thicknessRatio – however no further resolution is performed to actually query the value of this attribute.

What am I missing here?


EDIT:

Here is the complete github project for this issue.

Answer

Apparently, it’s not possible to correctly reference attributes in xml drawables.

The workaround is to create different drawables (one with the small ratio and one with the large) and use these in your separate styles. Then you can apply those styles to your views.

I’ve sent you a pull request that demonstrates this:

https://github.com/curioustechizen/so-question-android-custom-styles/pull/1

also see:

https://stackoverflow.com/a/13471695/1369222