Draw a hollow circle on an Android Canvas - android

I am working on creating a custom view where a user can select an angle. Below is an example of what the end result should look like:
I am achieving this with the following code:
mPaint.setColor(Color.BLACK);
canvas.drawCircle((int) (mSize.right / 2), (int) (mSize.bottom / 2),
mWidthOutside, mPaint);
mPaint.setColor(Color.LTGRAY);
canvas.drawCircle((int) (mSize.right / 2), (int) (mSize.bottom / 2),
mWidthInside, mPaint);
The problem with doing it this way, is the background is a static LTGRAY, which I hope to make Transparent.
How would I go about leaving the center of the circle transparent?
I have tried the following hoping the the drawArc function would only create a line the width of the paint, and not fill the center. It does in face fill the center.
RectF rectF = new RectF(centerX - mRadius, centerY - mRadius, centerX
+ mRadius, centerY + mRadius);
canvas.drawArc(rectF, 0, 360, false, mPaint);
Suggestions on how to keep the center of the circle transparent?

As asked :)
The solution is to set the style to be Paint.Style.STROKE

Hollow Circle in canvas.
Bitmap bitmap=Bitmap.createBitmap(500,500, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(bitmap);
//canvas.clipPath(,Region.Op.DIFFERENCE);
Paint paint=new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(140);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(250,250,150,paint);
imageView.setImageBitmap(bitmap);

Related

Transparency made from interstection

Pulling my hear out over this one. I have a background bitmap that I want to overlay another bitmap on top of that has transparent cutouts. I have no problem doing that if the cutout is a basic shape, but I need the cutout to be the intersection of two circles (sort of a leaf shape). I tried making a third bitmap to produce the a cutout template but I can't even get a clean representation of the cutout let alone get it to work as a cutout template.
Anyone know how to do something like this? Here is my (simplified) attempt:
#Override
public void draw(Canvas canvas) {
float w = canvas.getWidth();
float h = canvas.getHeight();
// just used to set some proportions
float off = 300f;
Paint paint = new Paint();
// make a background bitmap with a yellow to green gradient
Bitmap bitmapBkg = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
Canvas canvasBkg = new Canvas(bitmapBkg);
paint.reset();
paint.setShader(new LinearGradient(0, h/2 - off, 0, h/2 + off, Color.YELLOW, Color.GREEN, Shader.TileMode.CLAMP));
canvasBkg.drawRect(new RectF(0, 0, w, h), paint);
// make an overlay bitmap with a red to magenta gradient which will have the cutouts
Bitmap bitmapOver = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
Canvas canvasOver = new Canvas(bitmapOver);
paint.reset();
paint.setShader(new LinearGradient(0, h/2 - off, 0, h/2 + off, Color.RED, Color.MAGENTA, Shader.TileMode.CLAMP));
canvasOver.drawRect(new RectF(0, 0, w, h), paint);
// make a bitmap of intersecting circles to be used as the cutout shape
Bitmap bitmapCut = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
Canvas canvasCut = new Canvas(bitmapCut);
paint.reset();
paint.setColor(Color.BLACK);
//paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvasCut.drawCircle(w / 2 - (off / 2 ), h / 2, off, paint);
paint.reset();
paint.setColor(Color.BLACK);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvasCut.drawCircle(w / 2 + (off / 2), h / 2, off, paint);
// apply cutout to overlay
paint.reset();
paint.setColor(Color.BLACK);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
canvasOver.drawBitmap(bitmapCut, 0, 0, paint);
// draw background and overlay onto main canvas
paint.reset();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bitmapBkg, 0, 0, paint);
canvas.drawBitmap(bitmapOver, 0, 0, paint);
}
Here is an image of what I am getting:
What I am trying to get would have the outside portion also red-magenta; only the eye-shape in the middle should be yellow-green.
Turns out the trick was adding yet another layer.
// make a secondary overlay that cuts out the whole circles
Bitmap bitmapOver2 = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
Canvas canvasOver2 = new Canvas(bitmapOver2);
paint.reset();
paint.setShader(new LinearGradient(0, h / 2 - off, 0, h / 2 + off, Color.RED, Color.MAGENTA, Shader.TileMode.CLAMP));
canvasOver2.drawRect(new RectF(0, 0, w, h), paint);
paint.reset();
paint.setColor(Color.BLACK);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvasOver2.drawCircle(w / 2 - (off / 2), h / 2, off, paint);
canvasOver2.drawCircle(w / 2 + (off / 2), h / 2, off, paint);
Applying it like so:
// draw background and overlay onto main canvas
paint.reset();
paint.setColor(Color.BLACK);
canvas.drawBitmap(bitmapBkg, 0, 0, paint);
canvas.drawBitmap(bitmapOver2, 0, 0, paint);
canvas.drawBitmap(bitmapOver, 0, 0, paint);
Basically it is a bit of a trick. It draws the mid-tier backdrop twice, once with a full circle cutouts and the other with the eye-shape cutout. The two fit together just right to pull off the desired effect.
Of course #Rotwang, you are probably right. Using Path and arcTo() would be a much better solution. The only reason I avoided that approach is b/c arcTo() is an api 21+ feature. So far I've manged to keep the api to 17+. But if anyone else would like to provide an arcTo() solution for completeness that would be cool to compare.

Draw circular sector using Path

I would like to draw something like this:
So on each photo I would like to put a circular sector (cut arc) with black area. How can I achieve that with using e.g.
canvas.draw(Path path, Paint paint);
I tried below but it didn t work out as I would like to:
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.addCircle(getWidth() / 2, getHeight() / 2, getHeight() / 2, Path.Direction.CW);
path.addRect(0, getHeight() - 70, getWidth(), getHeight(), Path.Direction.CW);
You almost had it working. You just need to clip the canvas before drawing and use Path.FillType.INVERSE_EVEN_ODD to draw your sector:
// Limit the drawable region of the canvas (saving the state before)
canvas.save();
canvas.clipRect(new Rect(0, canvas.getHeight() - 70, canvas.getWidth(), canvas.getHeight()));
Path path = new Path();
path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
path.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, canvas.getHeight() / 2, Path.Direction.CW);
path.addRect(0, canvas.getHeight() - 70, canvas.getWidth(), canvas.getHeight(), Path.Direction.CW);
canvas.drawPath(path, paint);
// Restore the canvas to the saved state to remove clip
canvas.restore();
// Draw more things on the canvas...
Alternativelly, you can use Canvas.drawArc (documentation)
I assume from you question that you need to assign a fix height for your sector, so you will need to compute startAngle and sweepAngle (parameters of the drawArc method) based on that height (assuming a square image). Here is the sample code (API level 15 compatible):
int sectorHeigh = 70; // The height of your sector in pixels
// Compute the start angle based on your desired sector height
float startAngle = (float) Math.toDegrees(Math.asin((canvas.getHeight() / 2f - sectorHeigh) / (canvas.getHeight() / 2f)));
// Add the arc (calculating the sweepAngle based on startAngle)
canvas.drawArc(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), startAngle, 2 * (90 - startAngle), false, paint);
Another way to draw your sector using arcs is creating a Path object, add an arc using Path.addArc (documentation) and then use Canvas.drawPath (documentation) to draw it.

Converting a Circle To a Ring using Canvas?

I'm using a Custom View where I created a Circle:
canvas.drawCircle(width/2, width/2, radius, paint1);
Now, I want to empty the circle from inside, something like a ring.
Drawing another smaller white circle will raise another problem. I'm planning to make my view listen to user clicks. I want to listen to the clicks on the ring only!
Is there a way to exclude some part of the canvas?
In order to draw a ring (i.e., a circle that is NOT filled in but just has a border, with the area inside transparent) do this:
Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
myPaint.setStyle(Paint.Style.STROKE);
int strokeWidth=4; // or whatever
myPaint.setStrokeWidth(strokeWidth);
myPaint.setColor(0xffff0000); //color.RED
float radius=(float) (0.5*(w+h)*0.5);
canvas.drawCircle((float)0.5*w, (float)0.5*h, radius, myPaint);
The call to drawCircle can also be replaced by drawArc like the code below. Either approach will work. The secret sauce is to set the paint style to Paint.Style.STROKE.
RectF oval = new RectF(w/2 - radius, w/2 - radius, w/2 + radius, w/2 + radius);
canvas.drawArc(oval, 0, 360, false, myPaint);
To draw a ring, use drawArc :
RectF oval = new RectF(width/2 - radius, width/2 - radius, width/2 + radius, width/2 + radius);
canvas.drawArc(oval, 0, 360, false, paint1);
I worked around the problem by drawing an inner white circle and filtering the clicks based on the two radius lengths I had.
So, you can't draw an empty circle but you can make a circle to behave like an empty one.
Thanks.
this solution works:
Paint paintPath;
int circleRadius = (int)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources()
.getDisplayMetrics());
paintPath = new Paint();
paintPath.setStrokeWidth(circleRadius);
paintPath.setColor(Color.WHITE);
paintPath.setStyle(Paint.Style.FILL_AND_STROKE);
paintPath.setAntiAlias(true);
paintPath.setPathEffect(null);
paintPath.setRasterizer(null);
inside on draw
RectF rectF = new RectF(centerx - radius, centery - radius,
centerx + radius, centery + radius);
Path myPath = new Path();
for (int i = 0; i <= 360; i += 1)
{
myPath.addArc(rectF, i, 1);
}
canvas.drawPath(myPath, paintPath);
More short way:
draw = function() {
ctx.globalCompositeOperation = "lighter";
ctx.beginPath();
// ctx.fillStyle = "green";
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
// ctx.fill();
ctx.strokeStyle = "yellow";
ctx.stroke();
ctx.closePath();
}

Drawing mirror text on canvas

I'm trying to draw on a canvas a text and below another text that is the mirror of this text (that looks like shadow)
I'm using it in the "onDraw" method
Is there a simple way to do it?
Thanks in advance,
Lior
sure can. You will need to scale the canvas first. Try this out:
paint.setTextSize(44);
int cx = this.getMeasuredWidth() / 2;
int cy = this.getMeasuredHeight() / 2;
paint.setColor(Color.RED);
canvas.drawText("Hello", cx, cy, paint);
canvas.save();
canvas.scale(1f, -0.5f, cx, cy);
paint.setColor(Color.GRAY);
canvas.drawText("Hello", cx, cy, paint);
super.onDraw(canvas);
canvas.restore();
Try different values for the scale Y value to get effect you want.

Android: Drawing a basic circle outline to a RelativeLayout

I have a RelativeLayout that I want to contain 4 concentric circles (a radar effect, if you will). I'd like to be able to hide and display the RelativeLayout, so I'm wondering how exactly to use Canvas and Paint to draw a circle to the RelativeLayout. If someone could explain the lifecycle of how it works, that would be very helpful. Right now I've got:
setContentView(R.layout.applayout);
myRelLayout = (RelativeLayout) findViewById(R.id.RLRadar);
Canvas canvas = new Canvas();
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(0xFF000000);
circlePaint.setStyle(Style.STROKE);
canvas.drawCircle((float) centerX, (float) centerY, (float) (maxRadius / 4), circlePaint);
Am I on the right track? Do I need to convert the canvas to some sort of ImageView and add that to the RelativeLayout? Or am I completely off base here?
Thanks for any help!
Edit: Here's the working code.
// Add the radar to the RadarRL
Picture picture = new Picture();
Canvas canvas = picture.beginRecording(screenWidth, screenHeight);
// Draw on the canvas
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(0xFF000000);
circlePaint.setStyle(Style.STROKE);
circlePaint.setStrokeWidth((float) 5.0);
canvas.drawCircle((float) centerX, (float) centerY, (float) (maxRadius / 4), circlePaint);
canvas.drawCircle((float) centerX, (float) centerY, (float) ((3 * maxRadius) / 4), circlePaint);
canvas.drawCircle((float) centerX, (float) centerY, (float) (maxRadius / 2), circlePaint);
canvas.drawCircle((float) centerX, (float) centerY, (float) (maxRadius), circlePaint);
picture.endRecording();
If you don't want to create a custom view, you can use setBackgroundDrawable. For example, using a Picture and a PictureDrawable:
// Create the picture.
Picture picture = new Picture();
Canvas canvas = beginRecording(WIDTH, HEIGHT);
// Draw on the canvas.
picture.endRecording();
// Now set it as the background drawable.
PictureDrawable drawable = new PictureDrawable(picture);
relativeLayout = (RelativeLayout) findViewById(R.id.RLRadar);
relativeLayout.setBackgroundDrawable(drawable);
You have to create a Custom View and override the View.draw(Canvas) method. You can then use the canvas parameter to draw the radar effect. The view can then be placed in any layout.

Categories

Resources