androidandroid-viewandroid-custom-viewcustom-view

custom view - rounding rectangle's corners drawn by lines


I'm playing with custom views and I want to build a rounded rectangle using path.lineTo() and path.arcTo() methods.
So, the rectangle I want to get:

enter image description here

Normally I draw this with this block of code:

    RectF backReftf = new RectF();
    Path path = new Path();
    int width = getWidth();
    int height = getHeight();
    float curve = (float) (0.1 *  height);
    RectF backReftf = new RectF();
    backReftf.left = 0;
    backReftf.top = 0;
    backReftf.right = width;
    backReftf.bottom = height;
    path.addRoundRect(backReftf, curve, curve, Path.Direction.CW);
    canvas.drawPath(path, paint);

But I want to draw this with path.lineTo() and path.arcTo().

According to Docs about arcTo():

Append the specified arc to the path as a new contour. If the start of the path is different from the path's current last point, then an automatic lineTo() is added to connect the current contour to the start of the arc. However, if the path is empty, then we call moveTo() with the first point of the arc.

So theoretically my arc should start there, where line ends, so if I drew a line (left side of rectangle):

    float curve = (float) (0.1 *  height);
    path.moveTo(0,0);
    path.lineTo(0, height - curve);

then my arc should start from this point (0, height - curve), but where do I pass those arguments when arcTo() has following parameters: arcTo (float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) ?

Plus how do I calculate in this case startAngle and sweepAngle?

Thanks in advance!


Solution

  • When drawing an arc, you need to specify the full bounding box for that arc, and the start and sweep angle. I try to see them visually, like so:

    arcTo angles

    E.g. when going clock wise, the start angle is positioned 180degrees from the origin. And from the startAngle, if you sweep 90 degrees clock wise, you'll end up at the desired end position.

    Take note where the origin, startAngle and sweepAngle are in this graphic. In kotlin, it can look something like this:

    // Given some radius, viewWidth and viewHeight
    override fun onDraw(canvas: Canvas?) {
            super.onDraw(canvas)
            path.apply {
                moveTo(radius, 0F)
                lineTo(viewWidth - radius, 0F)
                arcTo(viewWidth - 2 * radius, 0F, viewWidth, 2 * radius, -90F, 90F, false)
                lineTo(viewWidth, radius)
                arcTo(viewWidth - 2 * radius, viewHeight - 2 * radius, viewWidth, viewHeight, 0F, 90F, false)
                lineTo(radius, viewHeight)
                arcTo(0F, viewHeight - 2 * radius, 2 * radius, viewHeight, 90F, 90F, false)
                lineTo(0F, radius)
                arcTo(0F, 0F, 2 * radius, 2 * radius, 180F, 90F, false)
            }
    
            canvas?.drawPath(path, linePaint)
        }
    

    And the result will be something like this:

    enter image description here