How to use android canvas to draw a Rectangle with only topleft and topright corners round?

前端 未结 15 2070
后悔当初
后悔当初 2020-11-29 01:58

I found a function for rectangles with all 4 corners being round, but I want to have just the top 2 corners round. What can I do?

canvas.drawRoundRect(new Re         


        
相关标签:
15条回答
  • 2020-11-29 02:31

    Here is my answer to the above question. Here, I have created Kotlin extension function which uses Path along with the quadTo function which can be used in lower-level APIs also.

    fun Canvas.drawRoundRectPath(
    rectF: RectF,
    radius: Float,
    roundTopLeft: Boolean,
    roundTopRight: Boolean,
    roundBottomLeft: Boolean,
    roundBottomRight: Boolean,
    paint: Paint) {
    
    val path = Path()
    
    //Move path cursor to start point
    if (roundBottomLeft) {
        path.moveTo(rectF.left, rectF.bottom - radius)
    } else {
        path.moveTo(rectF.left, rectF.bottom)
    }
    
    // drawing line and rounding top left curve
    if (roundTopLeft) {
        path.lineTo(rectF.left, rectF.top + radius)
        path.quadTo(rectF.left, rectF.top, rectF.left + radius, rectF.top)
    } else {
        path.lineTo(rectF.left, rectF.top)
    }
    
    // drawing line an rounding top right curve
    if (roundTopRight) {
        path.lineTo(rectF.right - radius, rectF.top)
        path.quadTo(rectF.right, rectF.top, rectF.right, rectF.top + radius)
    } else {
        path.lineTo(rectF.right, rectF.top)
    }
    
    // drawing line an rounding bottom right curve
    if (roundBottomRight) {
        path.lineTo(rectF.right, rectF.bottom - radius)
        path.quadTo(rectF.right, rectF.bottom, rectF.right - radius, rectF.bottom)
    } else {
        path.lineTo(rectF.right, rectF.bottom)
    }
    
    // drawing line an rounding bottom left curve
    if (roundBottomLeft) {
        path.lineTo(rectF.left + radius, rectF.bottom)
        path.quadTo(rectF.left, rectF.bottom, rectF.left, rectF.bottom - radius)
    } else {
        path.lineTo(rectF.left, rectF.bottom)
    }
    path.close()
    
    drawPath(path, paint)
    }
    

    We can call the function with canvas object and pass the RectF with the dimension on which we want to apply the curve.

    Also, we can pass the boolean for the corners which we want to round. This answer can further be customized to accept radius for individual corners.

    0 讨论(0)
  • 2020-11-29 02:32

    Use a path. It has the advantage of working for APIs less than 21 (Arc is also limited thusly, which is why I quad). Which is a problem because not everybody has Lollipop yet. You can however specify a RectF and set the values with that and use arc back to API 1, but then you wouldn't get to use a static (without declaring a new object to build the object).

    Drawing a rounded rect:

        path.moveTo(right, top + ry);
        path.rQuadTo(0, -ry, -rx, -ry);
        path.rLineTo(-(width - (2 * rx)), 0);
        path.rQuadTo(-rx, 0, -rx, ry);
        path.rLineTo(0, (height - (2 * ry)));
        path.rQuadTo(0, ry, rx, ry);
        path.rLineTo((width - (2 * rx)), 0);
        path.rQuadTo(rx, 0, rx, -ry);
        path.rLineTo(0, -(height - (2 * ry)));
        path.close();
    

    As a full function:

    static public Path RoundedRect(float left, float top, float right, float bottom, float rx, float ry, boolean conformToOriginalPost) {
        Path path = new Path();
        if (rx < 0) rx = 0;
        if (ry < 0) ry = 0;
        float width = right - left;
        float height = bottom - top;
        if (rx > width/2) rx = width/2;
        if (ry > height/2) ry = height/2;
        float widthMinusCorners = (width - (2 * rx));
        float heightMinusCorners = (height - (2 * ry));
    
        path.moveTo(right, top + ry);
        path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
        path.rLineTo(-widthMinusCorners, 0);
        path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
        path.rLineTo(0, heightMinusCorners);
    
        if (conformToOriginalPost) {
            path.rLineTo(0, ry);
            path.rLineTo(width, 0);
            path.rLineTo(0, -ry);
        }
        else {
            path.rQuadTo(0, ry, rx, ry);//bottom-left corner
            path.rLineTo(widthMinusCorners, 0);
            path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner
        }
    
        path.rLineTo(0, -heightMinusCorners);
    
        path.close();//Given close, last lineto can be removed.
    
        return path;
    }
    

    You'd want to line all the way to those corner bits, rather than quad across them. This is what setting true to conformToOriginalPost does. Just line to the control point there.

    If you want to do that all but don't care about pre-Lollipop stuff, and urgently insist that if your rx and ry are high enough, it should draw a circle.

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    static public Path RoundedRect(float left, float top, float right, float bottom, float rx, float ry, boolean conformToOriginalPost) {
        Path path = new Path();
        if (rx < 0) rx = 0;
        if (ry < 0) ry = 0;
        float width = right - left;
        float height = bottom - top;
        if (rx > width/2) rx = width/2;
        if (ry > height/2) ry = height/2;
        float widthMinusCorners = (width - (2 * rx));
        float heightMinusCorners = (height - (2 * ry));
    
        path.moveTo(right, top + ry);
        path.arcTo(right - 2*rx, top, right, top + 2*ry, 0, -90, false); //top-right-corner
        path.rLineTo(-widthMinusCorners, 0);
        path.arcTo(left, top, left + 2*rx, top + 2*ry, 270, -90, false);//top-left corner.
        path.rLineTo(0, heightMinusCorners);
        if (conformToOriginalPost) {
            path.rLineTo(0, ry);
            path.rLineTo(width, 0);
            path.rLineTo(0, -ry);
        }
        else {
            path.arcTo(left, bottom - 2 * ry, left + 2 * rx, bottom, 180, -90, false); //bottom-left corner
            path.rLineTo(widthMinusCorners, 0);
            path.arcTo(right - 2 * rx, bottom - 2 * ry, right, bottom, 90, -90, false); //bottom-right corner
        }
    
        path.rLineTo(0, -heightMinusCorners);
    
        path.close();//Given close, last lineto can be removed.
        return path;
    }
    

    So, conformToOriginalPost actually draws a rounded rect without the bottom two bits rounded.

    0 讨论(0)
  • 2020-11-29 02:32
    public static Path composeRoundedRectPath(RectF rect, float topLeftDiameter, float topRightDiameter,float bottomRightDiameter, float bottomLeftDiameter){
        Path path = new Path();
        topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
        topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
        bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
        bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;
    
        path.moveTo(rect.left + topLeftDiameter/2 ,rect.top);
        path.lineTo(rect.right - topRightDiameter/2,rect.top);
        path.quadTo(rect.right, rect.top, rect.right, rect.top + topRightDiameter/2);
        path.lineTo(rect.right ,rect.bottom - bottomRightDiameter/2);
        path.quadTo(rect.right ,rect.bottom, rect.right - bottomRightDiameter/2, rect.bottom);
        path.lineTo(rect.left + bottomLeftDiameter/2,rect.bottom);
        path.quadTo(rect.left,rect.bottom,rect.left, rect.bottom - bottomLeftDiameter/2);
        path.lineTo(rect.left,rect.top + topLeftDiameter/2);
        path.quadTo(rect.left,rect.top, rect.left + topLeftDiameter/2, rect.top);
        path.close();
    
        return path;
    }
    
    0 讨论(0)
提交回复
热议问题