How to modify drawables programmatically on Android?
Welcome to my problem-solving blog series! I will describe here common obstacles, which you can face while developing Android apps, and try to give you simple solutions to overcome these difficulties.
Problem
Let’s say we would like to create a simple app which displays a list of colors. Each item in that list contains a name on the left and a little circle with that color on the right. To tweak it a little bit, we want our color to be displayed as a gradient. Sounds simple, right? So we start with creating a RecyclerView
, Adapter
then a ViewHolder
which will bind our entity, e.g. ColorItem
. We could end up having a code like this in our ViewHolder
:
And our background drawable resource would look like this:
What could possibly go wrong? Let’s build it and run.
Wait, something’s not right! All drawables have the background color of the last bound item, even though I bind the correct one for each item in the ViewHolder
.
Solution
To understand what happened here, we need to know one thing about Drawable
on Android. Whenever we create a View
which has a background set to a resource Drawable
, it will create a new Drawable
instance, but they all share common state! It’s due to optimizations, to not create e.g. a bitmap for every single view that uses this resource. If you want to read more about it, here is a great blog post explaining everything.
Okay, so we know already what’s causing our problem. Now, what can we do to solve it? Luckily for us, there’s a simple method that we can use to tell Drawable
to have its own state and not to share it with other Drawable
instances. It’s called Drawable.mutate()
and you can read more about it in the documentation.
Here’s the updated code:
Let’s build and run it.
Success! That’s the result we were expecting. Since we are using Kotlin here, we can create a nice looking DSL for Drawable
mutations. So the final version of our code would look like this:
Conclusion
We need to remember about these pitfalls of Android framework optimizations when modifying our Drawable
objects programmatically. Otherwise, it can lead to unpredictable behaviors, like the one we saw above. Hopefully, I saved you some time on googling and wondering what’s wrong with your code. I’m looking forward to your feedback and wait patiently for the next posts in my problem-solving blog series! :)