setColorFilter doesn't seem to work on kitkat

2019-06-26 07:15发布

I'm attempting to colorize a graphic using setColorFilter. The following code seems to work fine on lollipop, but it seems to have no effect on kitkat, the icon is rendered in it's 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();

The mutate and invalidateSelf calls don't seem to have any effect on the problem here, just leaving them in as an example of part of what's been tried to figure out what's going on.

FWIW, I'm using the drawable as part of a LayerDrawable in a StateListDrawable that gets used as either the background for a button or as the drawable for an ImageView The results are consistent (ie., wrong on kitkat) either way. I've also tried putting the icon drawable directly into the StateListDrawable again with no change in behavior. In all cases, it works fine on lollipop, but doesn't work on kitkat.

As an experiment, I took the tinted Drawable out of the StateListDrawable but not the LayerDrawable and it works as expected. Apparently there's something flawed in KitKat's implementation of StateListDrawable that prevents it from working, that has been remedied in later versions.

2条回答
手持菜刀,她持情操
2楼-- · 2019-06-26 07:36

Ultimately, it seems like the problem is that KitKat doesn't support using a ColorFilter (or implicitly an alpha) on a Drawable that will in turn be in a StateListDrawable. My solution was to use the same to code to construct the complex Drawable and then render that into a simple 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);
}
查看更多
淡お忘
3楼-- · 2019-06-26 07:55

Rather than attach the coloring to something like "disabled" state (as in the accepted answer) I found the answer to be simpler by focusing on recoloring and let my usage leverage how to include the now-tinted image in the StateListDrawable. (And FYI, I've tried to translate from the Xamarin C# I'm using, but the below code may not complie 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'm rather foreign to Android development and can thank him for showing the pieces I needed before then simplifying them.

查看更多
登录 后发表回答