Dynamically changing SVG colours on Android

| 2 minute read | android

Recently, I needed to change the colour of part of a drawable - and I wanted to do it better than manually swapping out the whole image, or swapping in and out the changing part and overlaying this on top of a base image.

I knew I wanted the assets to be vector based if at all possible - SVG support was added in Lollipop with the VectorDrawable class and have been a great addition. Assets appear beautifully sharp, and gone are the days of adding multiple pngs to cater for different screen sizes.

I found that with custom attributes and themes, doing what I wanted could be achieved relatively easily.

Changing bauble colours on a Christmas tree

First, we create attributes for the two kinds of bauble, so we can change their colours:

<declare-styleable name="ChristmasTree">
<attr name="bauble_round" format="color" />
<attr name="bauble_small" format="color" />
</declare-styleable>
view raw attrs.xml hosted with ❤ by GitHub

Then, in the VectorDrawable, set the parts we want to dynamically change to use these attributes:

<path
android:fillColor="?attr/bauble_round"
android:pathData="...." />
<path
android:fillColor="?attr/bauble_small"
android:pathData="...." />
...

Create themes and set the colours you want to use:

<style name="UpdatedScene" parent="DefaultScene">
<item name="bauble_round">#db486e</item>
<item name="bauble_small">#22c7f7</item>
</style>
<style name="DefaultScene">
<item name="bauble_round">#fec758</item>
<item name="bauble_small">#f22424</item>
</style>
view raw styles.xml hosted with ❤ by GitHub

Use the drawable in an ImageView:

final ContextThemeWrapper wrapper = new ContextThemeWrapper(this, R.style.DefaultScene);
final Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.christmas, wrapper.getTheme());
imageView.setImageDrawable(drawable);
view raw usage.java hosted with ❤ by GitHub

That’s it! When you want to change the colours, simply set a different theme and your drawable will update.

See the GitHub repo for a full sample.

Update: If you specify each attribute within a different theme, you can also choose which to apply at runtime by creating a Theme dynamically. This is useful when you know at compile time what colours a certain element are, but don’t know until runtime which need to be updated.

final Resources.Theme theme = getResources().newTheme();
theme.applyStyle(R.style.BaubleRound, false);
theme.applyStyle(R.style.BaubleSmall, false);
final Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.christmas, theme);
imageView.setImageDrawable(drawable);

Note: for every different part of your image you want to change, you need a different attribute - so if you happen to have an SVG made up of lots and lots of elements, and you want to change them all separately, this is probably not the best solution for you.

Additionally, this is v21+ only - the backwards compatibility support for VectorDrawables before 21 creates static pngs, so changing the theme at runtime will have no effect.


Special thanks to Mark Allison for the idea and the creator of the Christmas tree SVG used.

If you enjoyed reading this post, you can find me on twitter @emmaguy