I'm trying to create a custom View which works simple: there is a Bitmap which is revealed by arc path - from 0deg to 360deg. Degrees are changing with some FPS.
So I made a custom View with overridden onDraw()
method:
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
canvas.drawArc(arcRectF, -90, currentAngleSweep, true, arcPaint);
arcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, circleSourceRect, circleDestRect, arcPaint);
}
arcPaint
is initialized as follows:
arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setColor(Color.RED); // Color doesn't matter
Now, everything draws great, but... the background is BLACK in whole View.
If I set canvas.drawColor(..., PorterDuff.Mode.DST)
and omit canvas.drawBitmap()
- the arc is drawn properly on transparent background.
My question is - how to set PorterDuff
modes to make it work with transparency?
Of course bitmap
is 32-bit PNG with alpha channel.
Use this statement during initialization of the view
just save canvas and restore after
in this case first rectangle will as destination for https://developer.android.com/reference/android/graphics/PorterDuff.Mode and the second rectangle as source
PorterDuff.Mode.CLEAR
doesn't work with hardware acceleration. Just setWorks perfectly for me.
to solve undesired
PorterDuff
effectuse the simplest method at first, like the OP's problem, a
Path.arcTo(*, *, *, *, false)
is enough -- note it'sarcTo
, notaddArc
, and thefalse
meansno forceMoveTo
before adding arc -- there is no need forPorterDuff
.if you really need PorterDuff, mainly for complex color morphing, like blending gradients, don't draw color or shape or bitmap with PorterDuff filtering effect directly to the default canvas provided in
onDraw(Canvas)
, use some buffering/dest bitmap[s] with alpha channel--andsetHasAlpha(true)
-- to store the result from PorterDuff filtering, at last draw the bitmap to the default canvas without applying any filtering except matrix changing.here's a working example to create border blurred round image:
some notes: 1, this view extends
ImageView
notView
, there are some differences.2, why
drawWithLayers
--saveLayer
orsaveLayerAlpha
-- is unrecommended: a, they are uncertain, sometimes doesn't work right(show transparent as black), especially forView
whoesonDraw(Canvas)
is empty, whileImageView.onDraw(Canvas)
used aDrawable
to draw some; b, they are expensive, they allocateoff-screen bitmap
to store temporary drawing result, and there is no clear clues of any resource recycling mechanism.3, using your own bitmap[s], is better for customized resource recycling.
Some people said, it's impossible to used PorterDuff without allocation bitmap[s] every drawing, because the bitmap's width, height can't be determined before drawing/layout/measure.
you CAN use buffering bitmap[s] for PorterDuff drawing:
at first, allocate some big enough bitmap[s].
then, draw on the bitmap[s] with some matrix.
and the, draw the bitmap[s] into a dest bitmap with some matrix.
at last, draw the dest bitmap into canvas with some matrix.
some people recommend setLayerType(View.LAYER_TYPE_SOFTWARE, null), which is not an option for me, because it will cause onDraw(Canvas) to be called in a loop.
result image source image
If you have solid color background all you need to do is set Paint color to your background color. For example, if you have a white background you can do:
However, if you need to erase a line with a transparent background you try this: In order to draw with a transparent color you must use Paint setXfermode which will only work if you set a bitmap to your canvas. If you follow the steps below you should get the desired result.
Create a canvas and set its bitmap.
Now you should be able draw with a transparent color using:
Everything is ok in your code except one thing: you get black background because your window is opaque. To achieve transparent result you should draw on another bitmap. In your onDraw method please a create new bitmap and do all the staff on it. After that draw this bitmap on your canvas.
For details and sample code please read this my answer: