Android: looking for a drawArc() method with inner & outer radius
Asked Answered
M

5

36

I have the following custom view:

alt text

This I have achieved by using the Canvas' drawArc() method. However, with this drawArc() method I cannot limit the arc's inner radius.

What I'd like to have is something like this:

alt text

where there is only an outer ring left.

What I need is an drawArc() function where I can set the inner radius of the arc. Anyone an idea how to do that?

(BTW, overpainting the inner area doesn't work, because it needs to be transparent. Painting an inner circle with Color.TRANSPARENT after painting the red and blue cones doesn't remove the old color. It just puts another layer on top, which is transparent and through which I can still see the red and blue)

Mendelevium answered 6/10, 2010 at 15:54 Comment(2)
Can you post your latest code after got your final requirement? ThanksZingale
Does any one know how to make smooth edges of meeting angles, as In my case when I am making ring with 4-5 equal distribution and different colors I can see background at the edgesAhner
B
33

You can paint over the inner area using the PorterDuff xfermode called "Clear." This will erase pixels.

Berni answered 6/10, 2010 at 17:4 Comment(4)
That's a possible solution. But what to do for the case, that there's already something drawn in the inner circle, which I don't want to get overpainted and neither to I want to erase the pixels and redraw it?Mendelevium
Draw the circle in an offscreen ARGB8888 bitmap, do the clear there, then draw the bitmap onto your Canvas. And that way you also don't have to redraw the circle every time.Berni
So, after looking into how to actually do this, I'm a bit lost: is an ARGB8888 bitmap a "normal" Bitmap goo.gl/ygTn - and how do I get the same drawing functions as I do with Canvas?Mendelevium
Update: I figured out that I can actually do it this way Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); and then draw on the Canvas, which will then write the actual pixels on the Bitmap.Mendelevium
D
76

You can do this:

    Paint paint = new Paint();
    final RectF rect = new RectF();
    //Example values
    rect.set(mWidth/2- mRadius, mHeight/2 - mRadius, mWidth/2 + mRadius, mHeight/2 + mRadius); 
    paint.setColor(Color.GREEN);
    paint.setStrokeWidth(20);
    paint.setAntiAlias(true);
    paint.setStrokeCap(Paint.Cap.ROUND);
    paint.setStyle(Paint.Style.STROKE);
    canvas.drawArc(rect, -90, 360, false, paint);

The key is in paint.setStyle(Paint.Style.STROKE);, it crops the arc's center with the stroke that you define in setStrokeWidth (in the example draws an arc with a radius of mRadius and 20px thick).

Hope it helps!

Disarray answered 1/4, 2011 at 14:54 Comment(5)
Works like a charm! Sometimes I forget to subtract the stroke from the container and I get a cropped circle. Thanks.Roman
And if you use paint.setStrokeCap(Paint.Cap.BUTT); instead of paint.setStrokeCap(Paint.Cap.ROUND); you get exactly what the question want.Molt
I think you can safely use canvas.drawArc(rect,0,360,false,paint); instead of -90Runlet
Can you set a Gradient color (like shader) to this?Shoop
Paint.Cap.BUTT is the default, so you might as well just delete the paint.setStrokeCap(); line.Hemiplegia
B
33

You can paint over the inner area using the PorterDuff xfermode called "Clear." This will erase pixels.

Berni answered 6/10, 2010 at 17:4 Comment(4)
That's a possible solution. But what to do for the case, that there's already something drawn in the inner circle, which I don't want to get overpainted and neither to I want to erase the pixels and redraw it?Mendelevium
Draw the circle in an offscreen ARGB8888 bitmap, do the clear there, then draw the bitmap onto your Canvas. And that way you also don't have to redraw the circle every time.Berni
So, after looking into how to actually do this, I'm a bit lost: is an ARGB8888 bitmap a "normal" Bitmap goo.gl/ygTn - and how do I get the same drawing functions as I do with Canvas?Mendelevium
Update: I figured out that I can actually do it this way Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); and then draw on the Canvas, which will then write the actual pixels on the Bitmap.Mendelevium
G
20
private static final float CIRCLE_LIMIT = 359.9999f;
/**
 * Draws a thick arc between the defined angles, see {@link Canvas#drawArc} for more.
 * This method is equivalent to
 * <pre><code>
 * float rMid = (rInn + rOut) / 2;
 * paint.setStyle(Style.STROKE); // there's nothing to fill
 * paint.setStrokeWidth(rOut - rInn); // thickness
 * canvas.drawArc(new RectF(cx - rMid, cy - rMid, cx + rMid, cy + rMid), startAngle, sweepAngle, false, paint);
 * </code></pre>
 * but supports different fill and stroke paints.
 * 
 * @param canvas
 * @param cx horizontal middle point of the oval
 * @param cy vertical middle point of the oval
 * @param rInn inner radius of the arc segment
 * @param rOut outer radius of the arc segment
 * @param startAngle see {@link Canvas#drawArc}
 * @param sweepAngle see {@link Canvas#drawArc}, capped at &plusmn;360
 * @param fill filling paint, can be <code>null</code>
 * @param stroke stroke paint, can be <code>null</code>
 * @see Canvas#drawArc
 */
public static void drawArcSegment(Canvas canvas, float cx, float cy, float rInn, float rOut, float startAngle,
        float sweepAngle, Paint fill, Paint stroke) {
    if (sweepAngle > CIRCLE_LIMIT) {
        sweepAngle = CIRCLE_LIMIT;
    }
    if (sweepAngle < -CIRCLE_LIMIT) {
        sweepAngle = -CIRCLE_LIMIT;
    }

    RectF outerRect = new RectF(cx - rOut, cy - rOut, cx + rOut, cy + rOut);
    RectF innerRect = new RectF(cx - rInn, cy - rInn, cx + rInn, cy + rInn);

    Path segmentPath = new Path();
    double start = toRadians(startAngle);
    segmentPath.moveTo((float)(cx + rInn * cos(start)), (float)(cy + rInn * sin(start)));
    segmentPath.lineTo((float)(cx + rOut * cos(start)), (float)(cy + rOut * sin(start)));
    segmentPath.arcTo(outerRect, startAngle, sweepAngle);
    double end = toRadians(startAngle + sweepAngle);
    segmentPath.lineTo((float)(cx + rInn * cos(end)), (float)(cy + rInn * sin(end)));
    segmentPath.arcTo(innerRect, startAngle + sweepAngle, -sweepAngle);
    if (fill != null) {
        canvas.drawPath(segmentPath, fill);
    }
    if (stroke != null) {
        canvas.drawPath(segmentPath, stroke);
    }
}

Can be extended to oval arc by duplicating rInn and rOut for x and y directions.

Also wasn't part of the question, but to draw a text in the middle of a segment:

textPaint.setTextAlign(Align.CENTER);
Path midway = new Path();
float r = (rIn + rOut) / 2;
RectF segment = new RectF(cx - r, cy - r, cx + r, cy + r);
midway.addArc(segment, startAngle, sweepAngle);
canvas.drawTextOnPath("label", midway, 0, 0, textPaint);
Glanville answered 10/8, 2014 at 12:3 Comment(4)
this code is amazing! However I have a question, is there way I can make the lines between the circles to have a different color than the circles perimeter color?Shoop
@Snake, you'll have to split segmentPath.*To() calls into multiple Paths and draw each separately with your select stroke colors. You can do what you discribe with 2 paths: moveTo,lineTo,moveTo,lineTo and moveTo,arcTo,moveTo,arcTo. You'll still have to keep the current one to have something to fill.Glanville
I "think" I vaguely understood it. Since I still require your help, I created another question as you suggested. Looking forward for your help. Here is the link #27851134Shoop
It is not necessary to use lineTo. For the second arcTo() add false as a final parameter; this will draw a line from the end of the first arc to the beginning of the second. Then close() the path.Gordan
C
3

drawing Circle and Arc. the following code is little dirty but it may help

        int sweepAngle sweepAngle = (360/7)%360;
    int startAngle = -90;
    int x = getWidth()/2;
    int y = getHeight()/2;
    int radius;
    radius = getWidth()/2-50;
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(50);
    paint.setColor(Color.WHITE);

    paint.setColor(Color.parseColor("#CD5C5C"));
    mBarPaintFill.setAntiAlias(true);

    canvas.drawCircle(x , y , radius, paint);
    paint.setColor(Color.BLUE);
    for (int i = 1 ; i<=5 ; i++){

        canvas.drawArc(x-radius,y-radius,x+radius,y+radius,startAngle,sweepAngle,false,paint);
        startAngle = (startAngle + sweepAngle+20)%360;
    }

enter image description here

Canaday answered 25/2, 2019 at 7:34 Comment(0)
D
2

You can try following ShapeDrawable

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
    <shape android:shape="oval" >
        <size
            android:height="56dp"
            android:width="56dp" />

        <stroke
            android:width="10dp"
            android:color="#0000ff" />
    </shape>
</item>
<item>
    <shape android:shape="oval" >
        <size
            android:height="24dp"
            android:width="25dp" />

        <stroke
            android:dashGap="10dp"
            android:dashWidth="10dp"
            android:width="10dp"
            android:color="#FF0000" />
    </shape>
</item>

Devoted answered 7/4, 2015 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.