14

I'm trying to reuse primitive shapes and compose much of my user interface using these declarative XML elements.

How to make a variable Android attribute?

But I do not want to create a separate XML file for each attribute value, and their permutations, and in the process duplicate much of the work.

For instance, I would like the consumer of this shape be able to define the android:radius value?

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
            android:startColor="#449def"
            android:endColor="#2f6699"
            android:angle="270"/>
    <stroke
            android:width="1dp"
            android:color="#2f6699"/>
    <corners
            android:radius="3dp"/>
</shape>

Set attribute from consuming XML parent?

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/shape_box_round_blue_uniform" />
    <!-- How to set the corner radius here? -->
    <item android:drawable="@drawable/shape_box_round_blue" />
</selector>

Solutions?

  • If at all possible, I would like to not use any Java code-behind / avoid the need to create custom controls / classes
  • Using dimension resources may be a fruitful avenue?
Cel
  • 5,879
  • 8
  • 66
  • 107
  • 1
    Setting the attribute in the xml parent is not doable AFAIK. Dimension resources can come handy -e.g. radius="@dimen/radius_round_blue" and define this radius_round_blue in `values/dimensions.xml`. This might be useful especially if this attribute varies according to some values identifier such as locale or screen size. However, to have complete freedom, you should create java classes but that again will cost extra coding (to set these drawables and bla) – Sherif elKhatib Mar 10 '13 at 20:05
  • @SherifelKhatib I'd be interested in what might be a good code-based solution then? a custom shape class that depends on a project-specific static theme class which may define non-default attribute values that the shape class should use, if present? or is it possible to change dimension resource values from code? – Cel Mar 10 '13 at 22:58
  • 3
    It seems like you are making an arbitrary restriction from doing things "the android way". The easy way to do this is with a custom class to which you pass parameters using an attr.xml file. Here is a nice example of how to do it: http://stackoverflow.com/questions/4495511/how-to-pass-custom-component-parameters-in-java-and-xml If you submit to the "android way" you will have a much more pleasant coding experience on this environment. It was tough for me to finally figure that out. – HalR Mar 12 '13 at 06:14
  • @HalR Thanks! This is great stuff, could you confirm whether it is possible to create a custom Selector as well in this case, and pass custom attributes to the Selector which would then modify its items accordingly (in code)?? If Selector cannot be targeted, then are you saying I should create custom shapes and instantiate them from code and programmatically make them part of the selector somehow? I'm not sure I see how the custom shape custom attributes helps me not duplicated XML files here.. In any case, please set your comment as an answer and you might earn the bounty :) – Cel Mar 15 '13 at 11:02
  • 1
    Seems this is bordering on an approach for xaml instead of android xml. There really isn't an Expression Blend to create custom objects to manipulate them for use as consumers in Android. The closest one can come is the solution provided by HaIR, very cool btw. Android uses ShapeDrawable to create the drawables from the xml file. The corners attribute only applies when the shape is defined as a rectangle. Either way there is not getting around having some Java code. It's not WPF,check [this](http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape) for more information. – Sean Mar 18 '13 at 14:46
  • @SherifelKhatib, post your comment as an answer please. – s.maks Apr 02 '13 at 13:53

1 Answers1

4

You can create

style attributes

, which I think is what you are looking for. They basically are variable attributes. E.g. you can use this in your themes.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- You can define attributes here -->
    <attr name="button_radius" format="reference" />
</resources>

Which defines a variable reference called: button_radius This can be used in Styles, or in layout xml files:

<!-- You can use them like so: -->
<style name="MyApp.Theme1" parent="android:Theme.Holo.Light">
    <item name="button_radius">12</item>
</style>

<style name="MyApp.Theme2" parent="android:Theme.Holo.Light">
    <item name="button_radius">36</item>
</style>

This way, by changing the theme, you can use the different values for your radius. Below is an UNTESTED example of how you would change your shape drawable:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="#449def"
        android:endColor="#2f6699"
        android:angle="270"/>
    <stroke
        android:width="1dp"
        android:color="#2f6699"/>
    <corners
        android:radius="?button_radius"/> <!-- NOTE the ?button_radius-->
</shape>

Users can simply apply a different style to use different attributes. I don't know if this example answers your question completele, but there's a lot you can do by declaring attributes in your themes. These attributes are dynamic references. For more info about the possibilities see this article

Entreco
  • 11,914
  • 8
  • 70
  • 88
  • While the reference attribute approach does not exactly answer my question, which was how to set the variable from parent selector, and different selectors would set different local values in the same project, your approach does allow project-specific global variables, getting half-way there! – Cel Apr 03 '13 at 09:17