Search code examples
androidandroid-layoutandroid-drawableandroid-attributes

How to customize custom drawable defined in XML at runtime?


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?


Solution

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