Search code examples
androidondrawandroid-vectordrawableporter-duff

How to correctly apply PorterDuff.Mode.MULTIPLY to a vector drawable in onDraw();


Inside the onDraw() method in my custom View, I am trying to overlay a rectangle on top of a vector drawable. The desired effect is for the top part of the eye to be violet, and the bottom part to remain grey, and the background to remain dark grey. To achieve this effect I'm using the PorterDuff.Mode.MULTIPLY.

Unfortunately, the effect that I am achieving is incorrect (the background of the vector drawable is affected, as if it didn't recognize it as a path):

Example of overlaying a Rect over a VectorDrawable

Here is my code:

public class EyeStatusIcon extends View {

    private Rect canvasRect = null;
    private Rect colorMask = null;
    private Drawable eyeIcon;
    private Paint maskPaint;

    public EyeStatusIcon(Context c){
        this(c,null);
    }

    public EyeStatusIcon(Context c, AttributeSet s) {
        super(c, s);

        eyeIcon = c.getDrawable(R.drawable.illustration_eye_open);

        maskPaint = new Paint();
        maskPaint.setStyle(Paint.Style.FILL);
        maskPaint.setColor(c.getColor(R.color.accent));

        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        eyeIcon.setBounds(canvasRect.left, canvasRect.top, canvasRect.right, canvasRect.bottom);
        eyeIcon.draw(canvas);
        canvas.drawRect(colorMask, maskPaint);


    }

    @Override
    protected void onSizeChanged(int newW, int newH, int oldW, int oldH) {
        canvasRect = new Rect(0, 0, newW, newH);
        colorMask = new Rect(0, 0, newW, newH/2);
        super.onSizeChanged(newW, newH, oldW, oldH);
    }
}

My question is, how can I apply PorterDuff blending (or another technique) in order to tint a part of my SVG-based, transparent-background Vector Drawable? Thank you!


Solution

  • I found a solution to my own problem by creating a second Canvas for the foreground, and cropping the bitmap (where it says getHeight()/2) drawn onto it. Hope this helps someone. This can be used to create a nice sliding courtain effect.

    public class EyeStatusIcon extends View {
    
        private Rect canvasRect = null;
        private Drawable normalIcon, tintedIcon;
    
        private Canvas foregroundCanvas;
        private Bitmap foregroundBitmap;
    
        public EyeStatusIcon(Context c){
            this(c,null);
        }
    
        public EyeStatusIcon(Context c, AttributeSet s) {
            super(c, s);
    
            setLayerType(LAYER_TYPE_HARDWARE, null);
    
            normalIcon = c.getDrawable(R.drawable.illustration_eye_open);
            tintedIcon = c.getDrawable(R.drawable.illustration_eye_open);
            if(tintedIcon != null) tintedIcon.setTint(c.getColor(R.color.accent));
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            normalIcon.setBounds(canvasRect.left, canvasRect.top, canvasRect.right, canvasRect.bottom);
            tintedIcon.setBounds(canvasRect.left, canvasRect.top, canvasRect.right, canvasRect.bottom);
    
            normalIcon.draw(canvas);
    
            tintedIcon.draw(foregroundCanvas);
            canvas.drawBitmap(foregroundBitmap, 0, 0, null);
    
        }
    
        @Override
        protected void onSizeChanged(int newW, int newH, int oldW, int oldH) {
            canvasRect = new Rect(0, 0, newW, newH);
    
            foregroundBitmap = Bitmap.createBitmap(getWidth(), getHeight()/2, Bitmap.Config.ARGB_8888);
            foregroundBitmap.eraseColor(Color.TRANSPARENT);
            foregroundCanvas = new Canvas(foregroundBitmap);
            foregroundCanvas.drawColor(Color.argb(0, 0, 0, 0));
    
            super.onSizeChanged(newW, newH, oldW, oldH);
        }
    
    }