Search code examples
androidandroid-canvas

How to draw segmented circle like this with certain requirements


I can't just seem to figure it out. I am trying to draw a segmented circle (what looks like circle inside a circle). However I want the segments to have specific colors and to be transparent inside the smaller circle. Preferably , I would like to make the color of the segmented lines different than the circle

enter image description here

Here are the solutions I had in mind:

1- Draw arc with fill color for the bigger circle and draw a circle for the small circle. 2 problems with this. First one is that the inner circle area is no longer transparent as it takes the color from the bigger one. Second problem is that the segmentation lines of the outer circle is going all the way to the center (not only to the inner circle perimeter)

2) Draw arcs for the bigger outer circle and draw circle for the inner circle. Set it to be color filled but don't show strokes. Then draw another outer circle on top with no fill just to show strokes. And then draw lines between the inner and outer circle using the calculations ( angle and radius) to determine where the lines are... Very convoluted solution, there has to be another way. Even with this solution, still have problem with the color showing in the center but maybe playing with gradient can help.

I read so much on SO but I couldn't figure the right answer as many answers would remove the control of circle parameters

HEELP!!!


Solution

  • @Override
    public void draw(Canvas canvas) {
        float size = Math.min(getWidth(),getHeight());
        paint.setStrokeWidth(size/4);
        paint.setStyle(Paint.Style.STROKE);
        final RectF oval = new RectF(0, 0, getWidth(), getHeight());
        oval.inset(size/8,size/8);
    
        paint.setColor(Color.RED);
        Path redPath = new Path();
        redPath.arcTo(oval, 0, 120, true);
        canvas.drawPath(redPath, paint);
    
        paint.setColor(Color.GREEN);
        Path greenPath = new Path();
        greenPath.arcTo(oval, 120, 120, true);
        canvas.drawPath(greenPath, paint);
    
        paint.setColor(Color.BLUE);
        Path bluePath = new Path();
        bluePath.arcTo(oval, 240, 120, true);
        canvas.drawPath(bluePath, paint);
    
        paint.setStrokeWidth(2);
        paint.setColor(0xff000000);
        canvas.save();
        for(int i=0;i<360;i+=40){
            canvas.rotate(40,size/2,size/2);
            canvas.drawLine(size*3/4,size/2,size,size/2,paint);
        }
        canvas.restore();
    
        final RectF ovalOuter = new RectF(0, 0, getWidth(), getHeight());
        ovalOuter.inset(1,1);
        canvas.drawOval(ovalOuter,paint);
    
        final RectF ovalInner = new RectF(size/4, size/4, size*3/4,size*3/4);
        canvas.drawOval(ovalInner,paint);
    }
    

    I'm drawing arcs using the Path class and strokes. Style.STROKE gives arcs without filling. Stroke width is set to size/4 which is a quarter of the view. Half of that stroke width goes outside and the second half goes inside, like this:

    xxxxxxxx outer border of the arc of width 5

    xxxxxxxx

    ------------ stroke

    xxxxxxxx

    xxxxxxxx inner border of the arc

    That's why I'm using insets - I need to offset the stroke a bit in order to fit it in the view. Without insets the arcs are cut by all four sides of the view.

    And why canvas rotation? Because it's easier to rotate the canvas with built-in methods than calculate lines manually. Rotation uses trigonometric functions and quickly becomes quite complex, hard to read and error prone. Basically I'm rotating the paper and drawing straight lines.

    enter image description here