How to change colors of a Drawable in Android?
Asked Answered
R

26

303

I'm working on an android application, and I have a drawable that I'm loading up from a source image. On this image, I'd like to convert all of the white pixels to a different color, say blue, and then cache the resultant Drawable object so I can use it later.

So for example say I have a 20x20 PNG file that has a white circle in the middle, and that everything outside the circle is transparent. What's the best way to turn that white circle blue and cache the results? Does the answer change if I want to use that source image to create several new Drawables (say blue, red, green, orange, etc)?

I'm guessing that I'll want to use a ColorMatrix in some way, but I'm not sure how.

Rhapsody answered 21/8, 2009 at 1:10 Comment(2)
Did you finally get this working in some way? I do see many answers down below, out of which I tried many as well, but nothing works. I currently have a white square, which I would like to color different every time based on need, so that I don't have to create static assets. Pls suggest, as I am still waiting for a working solution for my simple shape in full white color.Gibun
@Gibun I built a library called SillyAndroid that contains a versatile Coloring class and does all sorts of coloring for drawables and text. You can check it out at github.com/milosmns/silly-android. The class is located at /sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.javaMakedamakefast
R
31

I was able to do this with the following code, which is taken from an activity (the layout is a very simple one, just containing an ImageView, and is not posted here).

private static final int[] FROM_COLOR = new int[]{49, 179, 110};
private static final int THRESHOLD = 3;

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_colors);

    ImageView iv = (ImageView) findViewById(R.id.img);
    Drawable d = getResources().getDrawable(RES);
    iv.setImageDrawable(adjust(d));
}

private Drawable adjust(Drawable d)
{
    int to = Color.RED;

    //Need to copy to ensure that the bitmap is mutable.
    Bitmap src = ((BitmapDrawable) d).getBitmap();
    Bitmap bitmap = src.copy(Bitmap.Config.ARGB_8888, true);
    for(int x = 0;x < bitmap.getWidth();x++)
        for(int y = 0;y < bitmap.getHeight();y++)
            if(match(bitmap.getPixel(x, y))) 
                bitmap.setPixel(x, y, to);

    return new BitmapDrawable(bitmap);
}

private boolean match(int pixel)
{
    //There may be a better way to match, but I wanted to do a comparison ignoring
    //transparency, so I couldn't just do a direct integer compare.
    return Math.abs(Color.red(pixel) - FROM_COLOR[0]) < THRESHOLD &&
        Math.abs(Color.green(pixel) - FROM_COLOR[1]) < THRESHOLD &&
        Math.abs(Color.blue(pixel) - FROM_COLOR[2]) < THRESHOLD;
}
Rhapsody answered 11/11, 2009 at 14:52 Comment(4)
where do i get the Threshold or the FROM_COLOR from?Monograph
Those were just constants that I defined; I just edited the answer to include them.Rhapsody
thank you ;) tried but it doesn't fit the problem i have. tried the setColorFilter, and this works but there's a problem with scaling the .9.png image. so if you have a idea why, please answer my question. #5884981Monograph
Color filters are much easier.Floreneflorentia
S
239

I think you can actually just use Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). This would set white pixels to red but I don't think it would affect the transparent pixels.

See Drawable#setColorFilter

Stenography answered 29/4, 2011 at 20:41 Comment(7)
This will work good when drawable is single color, better when its white.Gerek
If the color is changed dinammicaly (for example in Adapter) the drawable must be mutable. Example: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY) more info: curious-creature.org/2009/05/02/drawable-mutationsShieh
Yup, it's especially good for highlighting (lighter, darker, or adding a hue to a a greyscale image.) I use this trick to toggle buttons where "unchecked" is greyscale & "checked" is a bold color from my app's color palette. Personally I find it easier than a custom checkbox.Stenography
This is exactly what I was looking for, though it's incredibly annoying that we can't do this in XML (except for on 5.0+). Tinting isn't even available in AppCompat, so we're stuck having to call setColorFilter every time we use the icons instead of having selectors with different color tints. Still, it's a much better solution than editing pngs directly and having extra static assets.Ginelle
Multiply will not work if your source icon has a dark color. To paint the source icon shape with the destination color use SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);Vesicle
For Xamarin Forms - Drawable.SetColorFilter(Android.Graphics.Color.Black, Mode.Multiply);Blaseio
setColorFilter() is deprecated now, see #56716593Fatma
B
160

Give this code a try:

ImageView lineColorCode = (ImageView)convertView.findViewById(R.id.line_color_code);
int color = Color.parseColor("#AE6118"); //The color u want             
lineColorCode.setColorFilter(color);
Bambi answered 30/6, 2012 at 15:35 Comment(0)
G
120

I know this question was ask way before Lollipop but I would like to add a nice way to do this on Android 5.+. You make an xml drawable that references the original one and set tint on it like such:

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_back"
    android:tint="@color/red_tint"/>
Gittel answered 9/12, 2014 at 17:6 Comment(5)
is this part of the latest support library too?Lament
No. That only helps with a few simple widget.Gittel
Tint is in support-v4 via DrawableCompatGiselagiselbert
Cool I'll look into that and update this accordingly.Gittel
Fresco doesn't support this type of drawableJenny
C
73

The new support v4 bring tint back to api 4.

you can do it like this

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Cordula answered 20/6, 2015 at 1:42 Comment(3)
Starting from support library 22.Damnatory
This is THE preferred solution, tinting drawables has been a grey area in older APIs since lollipop was released. This brakes that barrier! I didn't know about this one - thanks @CordulaYeseniayeshiva
Be careful! You should mutate your new wrapped drawable "#mutate()" to avoid state related issues. See https://mcmap.net/q/99592/-how-to-change-colors-of-a-drawable-in-androidKidron
B
64

If you have a drawable that's a solid color and you want to change it to a differnet solid color, you can use a ColorMatrixColorFilter. Transparency is preserved.

int iColor = Color.parseColor(color);

int red   = (iColor & 0xFF0000) / 0xFFFF;
int green = (iColor & 0xFF00) / 0xFF;
int blue  = iColor & 0xFF;

float[] matrix = { 0, 0, 0, 0, red,
                   0, 0, 0, 0, green,
                   0, 0, 0, 0, blue,
                   0, 0, 0, 1, 0 };

ColorFilter colorFilter = new ColorMatrixColorFilter(matrix);
drawable.setColorFilter(colorFilter);
Bawcock answered 23/6, 2012 at 17:23 Comment(4)
If you want to use a color resource rather than a string (#ff0000 etc), you can use e.g. int iColor = getResources().getColor(R.color.primary)Surculose
this works but I have checkbox and I want to preserve the white tick in the middle. Any suggestion for that?Nefen
The code in Ben's comment is now deprecated. Instead, you can use int iColor = ContextCompat.getColor(context, R.color.primary);.Gudrunguelderrose
@Mike Hill Ok, explain why you put more than 20 colors.You need insert greather then twenty colors in array because else it crashes with java.lang.ArrayIndexOutOfBoundsExceptionDoubtless
P
56

I also use ImageView for icons (in ListView or settings screen). But I think there is much simpler way to do that.

Use tint to change the color overlay on your selected icon.

In xml,

android:tint="@color/accent"
android:src="@drawable/ic_event" 

works fine since it comes from AppCompat

Providenciaprovident answered 19/9, 2015 at 11:55 Comment(3)
There are lots of good answers here, but for OP's question this is the best and simplest solution.Damselfly
for api 22 and aboveSexology
@philipoghenerobobalogun i saw this working on api 19Serdab
E
43

You should do this for all APIs:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
Endospore answered 7/3, 2013 at 9:1 Comment(4)
This solved the issue in a acceptable manner. But when filtering the color, it may happen (it happened to me) that the resulting color is not as expected. The color that was to lighten. What I did was : ` new LightingColorFilter(Color.parseColor("#FF000000"), myFinalColor) `Limerick
Emphasizing what I think the previous commenter is saying, this solution changes the colors if the 2 parameters in the LightingColorFilter are different, e.g., ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY); will change black to gray in the drawable.Fabio
This seems does not work when alpha is used for tint color.Bygone
As documentation shows it doesn't change alpha channel, so won't make anything more or less transparent: developer.android.com/reference/android/graphics/…Darceydarci
R
31

I was able to do this with the following code, which is taken from an activity (the layout is a very simple one, just containing an ImageView, and is not posted here).

private static final int[] FROM_COLOR = new int[]{49, 179, 110};
private static final int THRESHOLD = 3;

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_colors);

    ImageView iv = (ImageView) findViewById(R.id.img);
    Drawable d = getResources().getDrawable(RES);
    iv.setImageDrawable(adjust(d));
}

private Drawable adjust(Drawable d)
{
    int to = Color.RED;

    //Need to copy to ensure that the bitmap is mutable.
    Bitmap src = ((BitmapDrawable) d).getBitmap();
    Bitmap bitmap = src.copy(Bitmap.Config.ARGB_8888, true);
    for(int x = 0;x < bitmap.getWidth();x++)
        for(int y = 0;y < bitmap.getHeight();y++)
            if(match(bitmap.getPixel(x, y))) 
                bitmap.setPixel(x, y, to);

    return new BitmapDrawable(bitmap);
}

private boolean match(int pixel)
{
    //There may be a better way to match, but I wanted to do a comparison ignoring
    //transparency, so I couldn't just do a direct integer compare.
    return Math.abs(Color.red(pixel) - FROM_COLOR[0]) < THRESHOLD &&
        Math.abs(Color.green(pixel) - FROM_COLOR[1]) < THRESHOLD &&
        Math.abs(Color.blue(pixel) - FROM_COLOR[2]) < THRESHOLD;
}
Rhapsody answered 11/11, 2009 at 14:52 Comment(4)
where do i get the Threshold or the FROM_COLOR from?Monograph
Those were just constants that I defined; I just edited the answer to include them.Rhapsody
thank you ;) tried but it doesn't fit the problem i have. tried the setColorFilter, and this works but there's a problem with scaling the .9.png image. so if you have a idea why, please answer my question. #5884981Monograph
Color filters are much easier.Floreneflorentia
K
23

You can solve it using Android support compat libraries. :)

 // mutate to not share its state with any other drawable
 Drawable drawableWrap = DrawableCompat.wrap(drawable).mutate();
 DrawableCompat.setTint(drawableWrap, ContextCompat.getColor(getContext(), R.color.your_color))
Kidron answered 16/6, 2017 at 16:9 Comment(4)
@AmitabhaBiswas Why do you completely change to wrong my answer? Part by part. 1. getResources().getDrawable() is deprecated!! 2. I use support libraries because I don't want to care about Andorid Api versions. 3. I don't want to redraw Drawable.... If you want to show another approach write your own answer.Kidron
@AmitabhaBiswas Besides, the drawables are shared amongst all getDrawable from resources, hence the mutate() call is required to be able to change the tint of a drawable, without altering all the drawables associates with that resource ID.Kidron
This is the best answer! Wrapping drawable in an image view does not solve the question.Valene
.mutate() make my day. it's really work good if work in some item listCoaming
C
15

In your Activity you can tint your PNG image resources with a single colour:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    myColorTint();
    setContentView(R.layout.activity_main);
}

private void myColorTint() {
    int tint = Color.parseColor("#0000FF"); // R.color.blue;
    PorterDuff.Mode mode = PorterDuff.Mode.SRC_ATOP;
    // add your drawable resources you wish to tint to the drawables array...
    int drawables[] = { R.drawable.ic_action_edit, R.drawable.ic_action_refresh };
    for (int id : drawables) {
        Drawable icon = getResources().getDrawable(id);
        icon.setColorFilter(tint,mode);
    }
}

Now when you use the R.drawable.* it should be coloured with the desired tint. If you need additional colours then you should be able to .mutate() the drawable.

Ceremony answered 20/3, 2014 at 22:43 Comment(0)
P
6

If you have your drawable set to the ImageView you can do it with a 1 liner:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);
Phi answered 6/12, 2018 at 9:10 Comment(0)
E
5
view.getDrawable().mutate().setColorFilter(0xff777777, PorterDuff.Mode.MULTIPLY); 

Thanks to @sabadow

Efrainefram answered 12/9, 2016 at 17:7 Comment(1)
setColorFilter deprecatedMarillin
K
5

This code snippet worked for me:

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(getResources().getColor(R.color.your_color),PorterDuff.Mode.MULTIPLY);

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)
Kant answered 5/2, 2017 at 3:51 Comment(0)
E
5

Too late but in case someone need it:

   fun setDrawableColor(drawable: Drawable, color: Int) :Drawable {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            drawable.colorFilter = BlendModeColorFilter(color, BlendMode.SRC_ATOP)
            return drawable
        } else {
            drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
            return drawable
        }
    }
Ensor answered 4/6, 2020 at 11:32 Comment(0)
S
4

This works with everything with background:

Textview, Button...

TextView text = (TextView) View.findViewById(R.id.MyText);
text.setBackgroundResource(Icon);    
text.getBackground().setColorFilter(getResources().getColor(Color), PorterDuff.Mode.SRC_ATOP);
Strapped answered 10/2, 2015 at 9:56 Comment(0)
H
4

There are so many solution but nobody suggested if the color resource xml file already have color then we can pick directly from there also as below:

ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setColorFilter(getString(R.color.your_color));
Harlequin answered 20/11, 2016 at 14:6 Comment(0)
L
3

I just encountered the issue and solved it by replacing:

android:tint="@color/yellow_800"

to the following

app:tint="@color/yellow_800"
Leonelleonelle answered 19/4, 2022 at 20:19 Comment(0)
S
2

Check out this sample code "ColorMatrixSample.java"

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.graphics;

import com.example.android.apis.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public class ColorMatrixSample extends GraphicsActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }

    private static class SampleView extends View {
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private ColorMatrix mCM = new ColorMatrix();
        private Bitmap mBitmap;
        private float mSaturation;
        private float mAngle;

        public SampleView(Context context) {
            super(context);

            mBitmap = BitmapFactory.decodeResource(context.getResources(),
                                                   R.drawable.balloons);
        }

        private static void setTranslate(ColorMatrix cm, float dr, float dg,
                                         float db, float da) {
            cm.set(new float[] {
                   2, 0, 0, 0, dr,
                   0, 2, 0, 0, dg,
                   0, 0, 2, 0, db,
                   0, 0, 0, 1, da });
        }

        private static void setContrast(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, translate,
                   0, scale, 0, 0, translate,
                   0, 0, scale, 0, translate,
                   0, 0, 0, 1, 0 });
        }

        private static void setContrastTranslateOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   1, 0, 0, 0, translate,
                   0, 1, 0, 0, translate,
                   0, 0, 1, 0, translate,
                   0, 0, 0, 1, 0 });
        }

        private static void setContrastScaleOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, 0,
                   0, scale, 0, 0, 0,
                   0, 0, scale, 0, 0,
                   0, 0, 0, 1, 0 });
        }

        @Override protected void onDraw(Canvas canvas) {
            Paint paint = mPaint;
            float x = 20;
            float y = 20;

            canvas.drawColor(Color.WHITE);

            paint.setColorFilter(null);
            canvas.drawBitmap(mBitmap, x, y, paint);

            ColorMatrix cm = new ColorMatrix();

            mAngle += 2;
            if (mAngle > 180) {
                mAngle = 0;
            }

            //convert our animated angle [-180...180] to a contrast value of [-1..1]
            float contrast = mAngle / 180.f;

            setContrast(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x + mBitmap.getWidth() + 10, y, paint);

            setContrastScaleOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + mBitmap.getHeight() + 10, paint);

            setContrastTranslateOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + 2*(mBitmap.getHeight() + 10),
                              paint);

            invalidate();
        }
    }
}

The relevant API is available here:

Scever answered 27/10, 2009 at 23:7 Comment(1)
This does show how to use ColorMatrix, but I'm not seeing how to use it to get the results that I'm looking for.Rhapsody
M
1

Short example to change drawable color according to isWorking field.

My shape xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="@android:color/holo_blue_bright" />
    <corners android:radius="30dp" />
    <size
        android:height="15dp"
        android:width="15dp" />
</shape>

My method to change:

private Drawable getColoredDrawable(int drawableResId, boolean isworking) {
    Drawable d = getResources().getDrawable(R.drawable.shape);
    ColorFilter filter = new LightingColorFilter(
            isworking ? Color.GREEN : Color.RED,
            isworking ? Color.GREEN : Color.RED);
    d.setColorFilter(filter);
    return d;
}

Example of usage:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
Masjid answered 22/1, 2015 at 11:39 Comment(0)
H
1

Tested. This worked by using toArgb()

val drawableIcon = ContextCompat.getDrawable(context, R.drawable.ic_brush);
drawableIcon.setTint(Color.Red.toArgb())
Hammered answered 24/12, 2022 at 12:41 Comment(0)
L
0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

in XML /res/values/color.xml

<?xml version="1.0" encoding="utf-8">
<resources>
    <color name="colorRed">#ff0000</color>
</resoures> 

Java Code

int color = ContextCompat.getColor(context, R.color.colorRed);

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);
Labradorite answered 7/7, 2019 at 9:24 Comment(0)
O
0

It works for some simple drawables. I used it on a simple solid color rect shape with rounded corners and needed to change that color with different layouts.

Try this

android:backgroundTint="#101010"
Olive answered 19/6, 2020 at 12:49 Comment(0)
T
0

Try android:backgroundTint="@color/quantum_black_100"

Toomer answered 21/7, 2021 at 6:32 Comment(0)
B
0

Koltin solution using view binding:

binding.avatar.drawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(R.color.white, BlendModeCompat.SRC_ATOP)

This uses the latest version of the core androidx library.

Backwash answered 7/4, 2022 at 6:8 Comment(0)
N
0

Will help those who want to completely change the colors of Drawable

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/ic_memory"
    android:backgroundTint="@color/lime" />

Nuzzle answered 23/12, 2022 at 13:49 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Colleencollege
D
-2

It's very very simple when you use a library to do that for you. Try this library

You can call like this:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Detrimental answered 14/3, 2017 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.