71

我找到了一个所有 4 个角都是圆形的矩形的函数,但我只想让前 2 个角变成圆形。我能做些什么?

canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
4

16 回答 16

62

使用路径。它具有适用于少于 21 个 API 的优势(Arc 也因此受到限制,这就是我四边形的原因)。这是一个问题,因为不是每个人都有棒棒糖。但是,您可以指定一个 RectF 并使用它设置值并使用 arc 回到 API 1,但是您将无法使用静态(没有声明一个新对象来构建对象)。

绘制圆角矩形:

    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();

作为一个完整的功能:

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;
}

你想要一直排到那些角落位,而不是四边形穿过它们。这就是 conformToOriginalPost 设置为 true 的作用。直接到那里的控制点。

如果你想做这一切但不关心棒棒糖之前的东西,并紧急坚持如果你的 rx 和 ry 足够高,它应该画一个圆圈。

@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;
}

因此,conformToOriginalPost 实际上绘制了一个圆角矩形,而底部两位没有四舍五入。

arcquadimage

于 2015-02-22T08:03:46.890 回答
54

我会画两个矩形:

canvas.drawRect(new RectF(0, 110, 100, 290), paint);
canvas.drawRoundRect(new RectF(0, 100, 100, 200), 6, 6, paint);

或者类似的东西,你只需将它们重叠起来,这样上角就会是圆形的。最好你应该为此编写一个方法

于 2011-05-05T10:48:54.903 回答
47

对于 API 21 及更高版本,Path 类添加了一个新方法addRoundRect(),您可以像这样使用它。

corners = new float[]{
    80, 80,        // Top left radius in px
    80, 80,        // Top right radius in px
    0, 0,          // Bottom right radius in px
    0, 0           // Bottom left radius in px
};

final Path path = new Path();
path.addRoundRect(rect, corners, Path.Direction.CW);
canvas.drawPath(path, mPaint);

在科特林

val corners = floatArrayOf(
    80f, 80f,   // Top left radius in px
    80f, 80f,   // Top right radius in px
    0f, 0f,     // Bottom right radius in px
    0f, 0f      // Bottom left radius in px
)

val path = Path()
path.addRoundRect(rect, corners, Path.Direction.CW)
canvas.drawPath(path, mPaint)
于 2019-05-21T12:41:57.787 回答
31

我改变了这个 答案,这样你就可以设置你想要圆的角落和想要锐利的角落。也适用于前棒棒糖

用法示例

只有右上角和右下角是圆角的

 Path path = RoundedRect(0, 0, fwidth , fheight , 5,5,
                     false, true, true, false);
 canvas.drawPath(path,myPaint);

圆形矩形:

    public static Path RoundedRect(
            float left, float top, float right, float bottom, float rx, float ry,
               boolean tl, boolean tr, boolean br, boolean bl
    ){
        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);
        if (tr)
            path.rQuadTo(0, -ry, -rx, -ry);//top-right corner
        else{
            path.rLineTo(0, -ry);
            path.rLineTo(-rx,0);
        }
        path.rLineTo(-widthMinusCorners, 0);
        if (tl)
            path.rQuadTo(-rx, 0, -rx, ry); //top-left corner
        else{
            path.rLineTo(-rx, 0);
            path.rLineTo(0,ry);
        }
        path.rLineTo(0, heightMinusCorners);

        if (bl)
            path.rQuadTo(0, ry, rx, ry);//bottom-left corner
        else{
            path.rLineTo(0, ry);
            path.rLineTo(rx,0);
        }

        path.rLineTo(widthMinusCorners, 0);
        if (br)
            path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner
        else{
            path.rLineTo(rx,0);
            path.rLineTo(0, -ry);
        }

        path.rLineTo(0, -heightMinusCorners);

        path.close();//Given close, last lineto can be removed.

        return path;
    }
于 2016-02-27T11:03:30.603 回答
12

用 Kotlin 编写的简单辅助函数。

private fun Canvas.drawTopRoundRect(rect: RectF, paint: Paint, radius: Float) {
    // Step 1. Draw rect with rounded corners.
    drawRoundRect(rect, radius, radius, paint)

    // Step 2. Draw simple rect with reduced height,
    // so it wont cover top rounded corners.
    drawRect(
            rect.left,
            rect.top + radius,
            rect.right,
            rect.bottom,
            paint
    )
}

用法:

canvas.drawTopRoundRect(rect, paint, radius)
于 2017-11-05T20:38:05.870 回答
11

您可以使用 Path 轻松实现此目的:

val radiusArr = floatArrayOf(
    15f, 15f,
    15f, 15f,
    0f, 0f,
    0f, 0f
)
val myPath = Path()
myPath.addRoundRect(
    RectF(0f, 0f, 400f, 400f),
    radiusArr,
    Path.Direction.CW
)

canvas.drawPath(myPath, paint)
于 2019-12-16T10:03:42.337 回答
10
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;
}
于 2016-06-30T12:53:12.303 回答
7

我通过以下步骤实现了这一点。

这些是圆角矩形看起来整洁的先决条件

  • 边缘的半径必须等于(矩形的高度/2)。这是因为如果它有任何不同的值,那么曲线与矩形直线相交的地方就不会是

接下来是绘制圆角矩形的步骤。

  • 首先我们在左右两边画2个圆,半径=矩形的高度/2

  • 然后我们在这些圆之间画一个矩形,得到想要的圆角矩形。

我在下面发布代码

private void drawRoundedRect(Canvas canvas, float left, float top, float right, float bottom) {
    float radius = getHeight() / 2;
    canvas.drawCircle(radius, radius, radius, mainPaint);
    canvas.drawCircle(right - radius, radius, radius, mainPaint);
    canvas.drawRect(left + radius, top, right - radius, bottom, mainPaint);
}

现在这会产生一个非常漂亮的圆角矩形,如下所示在此处输入图像描述

于 2016-05-13T09:43:36.623 回答
6

这是一个老问题,但是我想添加我的解决方案,因为它使用本机 SDK,没有大量自定义代码或 hacky 绘图。API 1 支持此解决方案。

正确执行此操作的方法是创建一条路径(如其他答案中所述),但是先前的答案似乎忽略了addRoundedRect为每个角采用半径的函数调用。

变量

private val path = Path()
private val paint = Paint()

设置油漆

paint.color = Color.RED
paint.style = Paint.Style.FILL

使用大小更改更新路径

在不是 onDraw 的地方调用它,例如onMeasure视图或onBoundChange可绘制对象。如果它没有改变(比如这个例子),你可以把这段代码放在你设置油漆的地方。

val radii = floatArrayOf(
    25f, 25f, //Top left corner
    25f, 25f, //Top right corner
    0f, 0f,   //Bottom right corner
    0f, 0f,   //Bottom left corner
)

path.reset() //Clears the previously set path
path.addRoundedRect(0f, 0f, 100f, 100f, radii, Path.Direction.CW)

此代码创建一个 100x100 的圆角矩形,其顶角以 25 半径圆角。

绘制路径

将其onDraw用于视图或draw可绘制对象。

canvas.drawPath(path, paint)
于 2019-09-03T07:19:18.570 回答
5

绘制实心边的一种简单有效的方法是使用剪裁 - 矩形剪裁本质上是免费的,并且比自定义路径要编写的代码少得多。

如果我想要一个 300x300 的矩形,左上角和右上角四舍五入 50 像素,你可以这样做:

canvas.save();
canvas.clipRect(0, 0, 300, 300);
canvas.drawRoundRect(new RectF(0, 0, 300, 350), 50, 50, paint);
canvas.restore();

这种方法仅适用于 2 或 3 个相邻角的舍入,因此它的可配置性比基于路径的方法稍差,但使用圆形矩形更有效,因为 drawRoundRect() 是完全硬件加速的(即镶嵌成三角形)而 drawPath() 总是回退到软件渲染(软件绘制路径位图,并将其上传到 GPU 上缓存)。

对于不经常绘制的小幅绘图来说,这不是一个巨大的性能问题,但如果您正在为路径设置动画,那么软件绘制的成本可能会使您的帧时间更长,并增加丢帧的机会。路径掩码也会消耗内存。

如果您确实想使用基于路径的方法,我建议您使用 GradientDrawable 来简化代码行(假设您不需要设置自定义着色器,例如绘制位图)。

mGradient.setBounds(0, 0, 300, 300);
mGradient.setCornerRadii(new int[] {50,50, 50,50, 0,0, 0,0});

使用GradientDrawable#setCornerRadii(),您可以将任何角设置为任何圆角,并在状态之间合理地设置动画。

于 2017-08-22T00:19:22.560 回答
3

您可以drawLine()使用drawArc().Canvas

于 2011-05-05T10:47:25.073 回答
3

如果半径是高度的一半,则使用 Path#arcTo() 版本绘制圆边

fun getPathOfRoundedRectF(
    rect: RectF,
    topLeftRadius: Float = 0f,
    topRightRadius: Float = 0f,
    bottomRightRadius: Float = 0f,
    bottomLeftRadius: Float = 0f
): Path {
    val tlRadius = topLeftRadius.coerceAtLeast(0f)
    val trRadius = topRightRadius.coerceAtLeast(0f)
    val brRadius = bottomRightRadius.coerceAtLeast(0f)
    val blRadius = bottomLeftRadius.coerceAtLeast(0f)

    with(Path()) {
        moveTo(rect.left + tlRadius, rect.top)

        //setup top border
        lineTo(rect.right - trRadius, rect.top)

        //setup top-right corner
        arcTo(
            RectF(
                rect.right - trRadius * 2f,
                rect.top,
                rect.right,
                rect.top + trRadius * 2f
            ), -90f, 90f
        )

        //setup right border
        lineTo(rect.right, rect.bottom - trRadius)

        //setup bottom-right corner
        arcTo(
            RectF(
                rect.right - brRadius * 2f,
                rect.bottom - brRadius * 2f,
                rect.right,
                rect.bottom
            ), 0f, 90f
        )

        //setup bottom border
        lineTo(rect.left + blRadius, rect.bottom)

        //setup bottom-left corner
        arcTo(
            RectF(
                rect.left,
                rect.bottom - blRadius * 2f,
                rect.left + blRadius * 2f,
                rect.bottom
            ), 90f, 90f
        )

        //setup left border
        lineTo(rect.left, rect.top + tlRadius)

        //setup top-left corner
        arcTo(
            RectF(
                rect.left,
                rect.top,
                rect.left + tlRadius * 2f,
                rect.top + tlRadius * 2f
            ),
            180f,
            90f
        )

        close()

        return this
    }
}

两个圆角 三个圆角 三个圆角

于 2020-07-02T07:15:06.070 回答
2

用左圆角绘制圆形矩形

  private void drawRoundRect(float left, float top, float right, float bottom, Paint paint, Canvas canvas) {
        Path path = new Path();
        path.moveTo(left, top);
        path.lineTo(right, top);
        path.lineTo(right, bottom);
        path.lineTo(left + radius, bottom);
        path.quadTo(left, bottom, left, bottom - radius);
        path.lineTo(left, top + radius);
        path.quadTo(left, top, left + radius, top);
        canvas.drawPath(path, onlinePaint);
    }
于 2015-07-20T07:02:38.297 回答
2

使用PaintDrawable可能会更好:

    val topLeftRadius = 10
    val topRightRadius = 10
    val bottomLeftRadius = 0
    val bottomRightRadius = 0
    val rect = Rect(0, 0, 100, 100)
    val paintDrawable = PaintDrawable(Color.RED)
    val outter = floatArrayOf(topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
            bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius)
    paintDrawable.setCornerRadii(outter)
    paintDrawable.bounds = rect
    paintDrawable.draw(canvas)
于 2019-01-16T03:32:51.340 回答
2

这是我对上述问题的回答。在这里,我创建了 Kotlin 扩展函数,它PathquadTo可用于低级 API 的函数一起使用。

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)
}

我们可以使用 canvas 对象调用该函数,并传递RectF我们想要应用曲线的维度。

此外,我们可以为要圆角的角传递布尔值。可以进一步自定义此答案以接受各个角的半径。

于 2020-10-01T10:22:31.207 回答
1

也许下面的代码可以帮助你

        Paint p = new Paint();
        p.setColor(color);

        float[] corners = new float[]{
                15, 15,        // Top, left in px
                15, 15,        // Top, right in px
                15, 15,          // Bottom, right in px
                15, 15           // Bottom,left in px
        };

        final Path path = new Path();
        path.addRoundRect(rect, corners, Path.Direction.CW);
        // Draw 
        canvas.drawPath(path, p);
于 2021-08-27T08:50:26.640 回答