3

I made a custom, multi-layered drawable to act as the background for a button. Sometimes, I want part of this drawable layer to be blue. Sometimes I want it to be green. Point is, it's a variable, and I want it to be definable in the associated custom view XML.

Is this possible? How do I write a drawable in XML whose value I can determine at runtime?

custom_button.xml

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:insetLeft="@dimen/button_inset_horizontal_material"
    android:insetTop="@dimen/button_inset_vertical_material"
    android:insetRight="@dimen/button_inset_horizontal_material"
    android:insetBottom="@dimen/button_inset_vertical_material">
    <shape android:shape="rectangle">
        <corners android:radius="@dimen/control_corner_material" />
        <solid android:color="?attr/colorButtonNormal" />
        <padding android:left="@dimen/button_padding_horizontal_material"
            android:top="@dimen/button_padding_vertical_material"
            android:right="@dimen/button_padding_horizontal_material"
            android:bottom="@dimen/button_padding_vertical_material" />
    </shape>
</inset>

The line <solid android:color="?attr/colorButtonNormal" /> is what I want to set at runtime. I have my custom view for this class already receiving the color value I want to use here - how do I apply it to the XML of this drawable?

ZakTaccardi
  • 12,212
  • 15
  • 59
  • 107
  • Don't know if there is a way to modify the custom logic programatically at runtime. You might just have to create two custom_button.xml files and choose them appropriately when you want one over the other. – Jay Snayder Feb 24 '15 at 14:07
  • @JaySnayder I'm actually trying to create a library for the custom button view that lets me easily modify the value used in this drawable – ZakTaccardi Feb 24 '15 at 14:10
  • Extracting the shape into a drawable and maybe then you can change it: http://stackoverflow.com/questions/7164630/how-to-change-shape-color-dynamically – Javi Mollá Feb 24 '15 at 14:14

1 Answers1

4

Like this:

InsetDrawable drawable = (InsetDrawable) myButton.getBackground();
GradientDrawable shape = (GradientDrawable) drawable.getDrawable();
shape.setColor(Color.BLUE);

I made a custom, multi-layered drawable to act as the background for a button.

This assumes myButton is the button which you refer to above and has been defined with

android:background="@drawable/custom_button"

EDIT

For an API level 1 way to do this:

Make a custom_shape.xml drawable:

<shape android:shape="rectangle">
    <corners android:radius="@dimen/control_corner_material" />
    <solid android:color="?attr/colorButtonNormal" />
    <padding android:left="@dimen/button_padding_horizontal_material"
        android:top="@dimen/button_padding_vertical_material"
        android:right="@dimen/button_padding_horizontal_material"
        android:bottom="@dimen/button_padding_vertical_material" />
</shape>

Write a method to change the colour of this drawable and put an inset around it:

private void changeColor() {
    // Get shape from XML
    GradientDrawable shape = (GradientDrawable) getResources().getDrawable(R.drawable.custom_shape);
    shape.setColor(Color.BLUE);

    // Programmatically create Inset
    InsetDrawable drawable =  new InsetDrawable(shape,
            getResources().getDimensionPixelSize(R.dimen.button_inset_horizontal_material),
            getResources().getDimensionPixelSize(R.dimen.button_inset_vertical_material),
            getResources().getDimensionPixelSize(R.dimen.button_inset_horizontal_material),
            getResources().getDimensionPixelSize(R.dimen.button_inset_vertical_material));

    // Apply to button
    myButton.setBackground(drawable);
}
Ken Wolf
  • 23,133
  • 6
  • 63
  • 84
  • Can you explain this a little more? This would not be using the custom_button.xml drawable I have defined – ZakTaccardi Feb 24 '15 at 14:14
  • Yes it would. `getBackground()` returns the `InsetDrawable` you created in `custom_button.xml`, provided you have set it as the background for your button, as you stated. – Ken Wolf Feb 24 '15 at 14:15
  • Thanks. Hopefully this will work. It looks like gradientDrawable.getDrawable() is KITKAT or above though – ZakTaccardi Feb 24 '15 at 15:00
  • is there any way to do this using an Inset still? – ZakTaccardi Feb 24 '15 at 15:52
  • this library is meant to work on ICS or above, but 4.1 would be okay too. – ZakTaccardi Feb 24 '15 at 16:16
  • See my edit above. You need to create a seperate drawable just for the shape, then wrap the inset around it programmatically. – Ken Wolf Feb 24 '15 at 16:20
  • Thanks Ken. Why is it important that the background has been defined with `android:background="@drawable/custom_button"`. I have noticed that the ripple selector uses strange colors if you set the background programmatically, instead of via XML. I need to do it programmatically and not with XML. – ZakTaccardi Feb 24 '15 at 19:19
  • How are you making it "act as the background to a button" if not by setting the background to the drawable? What ripple selector? You don't mention in in your question. I feel I have answered your question as best as I can - perhaps someone else will come along and answer your follow up questions. Good luck! :) – Ken Wolf Feb 24 '15 at 19:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/71612/discussion-between-zaktaccardi-and-ken-wolf). – ZakTaccardi Feb 24 '15 at 20:05