SetColorFilter doesn't seem to work on kitkat
I am trying to colorize graphics using setColorFilter
. The following code seems to work fine for lollipop, but it doesn't seem to affect kitkat, the icon is displayed in its original colors:
Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_chat_button).mutate();
icon.setColorFilter(context.getResources().getColor(R.color.control_tint_color), PorterDuff.Mode.SRC_ATOP);
icon.invalidateSelf();
Calling mutate
and invalidateSelf
doesn't seem to affect the problem here, just leaving them as an example of a part of what was trying to figure out what was going on.
FWIW, I am using a drawable as part LayerDrawable
in StateListDrawable
which is used as the background for a button or as a highlight for ImageView
. The results are consistent (i.e. wrong on kitkat) anyway. I also tried putting the icon in again StateListDrawable
without changing the behavior. In all cases, it works fine on lollipop, but doesn't work on kitkat.
As an experiment, I took the toned Drawable
from StateListDrawable
but not LayerDrawable
, and it works as expected. Apparently there is some bug in KitKat implementation StateListDrawable
that prevents it from working, which was fixed in later versions.
source to share
Ultimately it seems like the problem is that KitKat doesn't support using ColorFilter
(or implicitly alpha) on Drawable
, which in turn will be on StateListDrawable
. My solution was to use the same code to build a complex Drawable
one and then render it into a simple one BitMapDrawable
:
static Drawable createDrawable(Context context, int color, boolean disabled) {
OvalShape oShape = new OvalShape();
ShapeDrawable background = new ShapeDrawable(oShape);
background.getPaint().setColor(color);
ShapeDrawable shader = new ShapeDrawable(oShape);
shader.setShaderFactory(new ShapeDrawable.ShaderFactory() {
@Override
public Shader resize(int width, int height) {
return new LinearGradient(0, 0, 0, height,
new int[]{
Color.WHITE,
Color.GRAY,
Color.DKGRAY,
Color.BLACK
}, null, Shader.TileMode.REPEAT);
}
});
Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_chat_button).mutate();
icon.setColorFilter(context.getResources().getColor(R.color.control_tint_color), PorterDuff.Mode.SRC_IN);
Drawable layer = new LayerDrawable(new Drawable[]{ shader, background, icon });
layer.setAlpha(disabled ? 128 : 255);
// Note that on KitKat, setting a ColorFilter on a Drawable contained in a StateListDrawable
// apparently doesn't work, although it does on later versions, so we have to render the colored
// bitmap into a BitmapDrawable and then put that into the StateListDrawable
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layer.setBounds(0, 0, layer.getIntrinsicWidth(), layer.getIntrinsicHeight());
layer.draw(canvas);
return new BitmapDrawable(context.getResources(), bitmap);
}
source to share
Rather than attaching the coloring to something like a "disabled" state (as in the accepted answer), I found the answer to be simpler, focusing on replay and letting my use be how to enable the rendered image in StateListDrawable
. (And FYI, I tried to translate from Xamarin C # which I am using, but the code below may not match correctly as Java)
static Drawable recolorDrawable(Drawable icon, int toColor)
{
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas myCanvas = new Canvas(bitmap);
icon.setColorFilter(toColor, PorterDuff.Mode.SRC_IN);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
icon.draw(myCanvas);
return new BitmapDrawable(context.getResources(), bitmap);
}
Finally, in all fairness to the accepted answer, I am quite foreign to Android development and can thank him for showing the parts I need and then simplifying them.
source to share