Change drawable color programmatically
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Android development, you often need to change the color of a drawable at runtime rather than defining separate XML resources for every color variation. This arises when supporting dynamic themes, responding to user preferences, or highlighting UI states like selection or error. Android provides several approaches for tinting drawables programmatically, each with different trade-offs around API level support and mutability.
Using setColorFilter
The setColorFilter method applies a color overlay to a drawable. It works on all API levels and is the simplest way to change a drawable's apparent color.
The PorterDuff.Mode.SRC_IN mode keeps the shape of the original drawable while replacing its color with the filter color. Other modes like MULTIPLY blend the filter with the original colors, which is useful for gradient drawables.
Using DrawableCompat.setTint
DrawableCompat.setTint from the AndroidX library is the recommended approach because it handles backward compatibility automatically. It delegates to the platform tint API on newer devices and falls back to a color filter on older ones.
You can also specify a tint mode with DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_IN) if the default mode does not produce the result you want.
Why mutate() Matters
Drawables loaded from resources share a common state by default. If you change the color of one instance, every view using that drawable resource sees the change. Calling mutate() gives the drawable its own independent state so that tinting one instance does not affect others.
Always call mutate() before changing color when multiple views might reference the same drawable resource.
Using ColorStateList for State-Aware Tinting
When a drawable needs to change color based on view state (pressed, focused, disabled), use a ColorStateList instead of a single color. This avoids manually tracking state changes yourself.
The order of state arrays matters. Android checks from top to bottom and uses the first match, so place the most specific states first and the default (empty) state last.
Common Pitfalls
- Forgetting to call
mutate(): Skippingmutate()causes shared drawable state across views, so tinting one icon unintentionally tints every instance of that resource. - Using deprecated
setColorFilter(int, Mode)on API 29 and above: The two-argument overload is deprecated. UsesetColorFilter(new PorterDuffColorFilter(color, mode))or switch toDrawableCompat.setTintinstead. - Applying a tint to a null drawable:
ContextCompat.getDrawable()can return null if the resource ID is invalid. Always null-check before calling tint methods to avoid a crash. - Incorrect PorterDuff mode: Using
SRC_OVERinstead ofSRC_INoverlays the color on top of the original rather than replacing it, producing a muddy or unexpected result on non-white drawables. - Placing the default state before specific states in ColorStateList: Android matches the first qualifying state array. If the empty (default) array comes first, the pressed and focused colors are never reached.
Summary
- Use
setColorFilterwithPorterDuff.Mode.SRC_INfor a quick, all-API-level color change on a drawable. - Prefer
DrawableCompat.setTintfrom AndroidX for backward-compatible tinting with cleaner API usage. - Always call
mutate()before modifying a drawable's color to avoid shared-state side effects across views. - Use
ColorStateListwithDrawableCompat.setTintListwhen the drawable color should respond to view states like pressed or focused. - Null-check every drawable obtained from resources before applying color changes.

