30

What would be the right way to turn a color Drawable into a grayscale one (to indicate disabled state)?

EDIT:
B/W => grayscale

yanchenko
  • 56,576
  • 33
  • 147
  • 165

4 Answers4

108

I know this question was asked a while ago, but I came across a simpler solution that works if you have a Drawable and you just want to display that same drawable in grayscale. No need to have a canvas or a painter...

protected Drawable convertToGrayscale(Drawable drawable)
{
    ColorMatrix matrix = new ColorMatrix();
    matrix.setSaturation(0);
    
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
    
    drawable.setColorFilter(filter);
    
    return drawable;
}
starball
  • 20,030
  • 7
  • 43
  • 238
Justin
  • 6,564
  • 6
  • 37
  • 34
  • 1
    Good tip ! Something interesting is that if you run this method by passing ContextCompat.getDrawable(context, R.drawable.myId); next time you need to get the same drawable, the color filter is already set. For my part I need to remove the color filter to get the color back on my drawable :) – Tobliug Mar 02 '16 at 10:20
  • 6
    Just mutate() your drawable before applying the color filter on it. Drawable properties are shared between instances so by mutating it, you are creating another "copy" of that drawable. Using color filter after mutating will be safe and will not affect your other drawable instances. –  Oct 24 '16 at 16:36
19

Apparently you can use the ColorMatrix class to do any sort of color-space transformations. It has a setSaturation() method that easily creates a color-to-grayscale transformation (zeroes saturation) for you.

So, you can use that filter to paint a new copy of the image. I haven't tried this, but it should work:

Bitmap grayscaleBitmap = Bitmap.createBitmap(
    colorBitmap.getWidth(), colorBitmap.getHeight(),
    Bitmap.Config.RGB_565);

Canvas c = new Canvas(grayscaleBitmap);
Paint p = new Paint();
ColorMatrix cm = new ColorMatrix();

cm.setSaturation(0);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(cm);
p.setColorFilter(filter); 
c.drawBitmap(colorBitmap, 0, 0, p);
intgr
  • 19,834
  • 5
  • 59
  • 69
3

Some coments to the answer of @intgr.
1. Bitmap.Config.ARGB_8888 to preserve the alpha-channel.
2. A little extra code:

//remember, you are converting a .png image, as opposed to a Drawable defined in .xml  
Bitmap colorBitmap = ((BitmapDrawable)drawable).getBitmap();    
// the code by intgr  
Drawable grayscaleDrawable = new BitmapDrawable(grayscaleBitmap);
Jarett Millard
  • 5,802
  • 4
  • 41
  • 48
yanchenko
  • 56,576
  • 33
  • 147
  • 165
  • 1
    Small Typo: Bitmap.Config.RGB_8888 should be Bitmap.Config.ARGB_8888. Works fine then, thanks. – Minsky May 30 '12 at 14:13
1

Are you specifically wanting to do this programatically and not just with disabled versions of the images? You could reference an XML drawable, something like:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_window_focused="false" android:state_enabled="false"
        android:drawable="@drawable/btn_default_normal_disable" />
    <item android:state_pressed="true" 
        android:drawable="@drawable/btn_default_pressed" />
    <item android:state_focused="true" android:state_enabled="true"
        android:drawable="@drawable/btn_default_selected" />
    <item android:state_enabled="true"
        android:drawable="@drawable/btn_default_normal" />
    <item android:state_focused="true"
        android:drawable="@drawable/btn_default_normal_disable_focused" />
    <item
         android:drawable="@drawable/btn_default_normal_disable" />
</selector>
Jeremy Logan
  • 47,151
  • 38
  • 123
  • 143