如何使用android画布绘制一个只有topleft和topright圆角的矩形?

vir*_*sir 47 android draw android-canvas

我找到了一个矩形函数,所有四个角都是圆的,但我想只有前两个角.我能做什么?

canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
Run Code Online (Sandbox Code Playgroud)

Tat*_*ize 51

使用路径.它具有为小于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();
Run Code Online (Sandbox Code Playgroud)

作为一个完整的功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

你想要一直排到那些角落位,而不是四角形.这是conformToOriginalPost设置为true的内容.只需到那里的控制点.

如果你想这样做但不关心前Lollipop的东西,并且迫切地坚持如果你的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;
}
Run Code Online (Sandbox Code Playgroud)

因此,conformToOriginalPost实际绘制一个圆角矩形,而底部的两位没有圆角.

arcquadimage


小智 43

我会画两个矩形:

canvas.drawRect(new RectF(0, 110, 100, 290), paint);
canvas.drawRoundRect(new RectF(0, 100, 100, 200), 6, 6, paint);
Run Code Online (Sandbox Code Playgroud)

或类似的东西,你只是重叠它们,以便上角是圆的.你最好为此写一个方法

  • 我需要在整个矩形中设置alpha,重叠部分与其他部分具有不同的alpha值 (8认同)
  • @ V.Kalyuzhnyu答案中使用的方法签名是自API 1以来可用的,你必须谈论`drawRoundRect(浮动左,浮顶,浮右,浮底,浮rx,浮ry,油漆)`这是可用的来自API 21. (4认同)

Moh*_*abi 26

我改变了这个 答案,这样你就可以设置你想要圆的哪个角,以及你想要哪个角.也适用于pre-lolipop

用法示例:

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

 Path path = RoundedRect(0, 0, fwidth , fheight , 5,5,
                     false, true, true, false);
 canvas.drawPath(path,myPaint);
Run Code Online (Sandbox Code Playgroud)

ROUNDRECT:

    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;
    }
Run Code Online (Sandbox Code Playgroud)


Vad*_*rov 11

用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
    )
}
Run Code Online (Sandbox Code Playgroud)

用法:

canvas.drawTopRoundRect(rect, paint, radius)
Run Code Online (Sandbox Code Playgroud)

  • 它将重叠,颜色alpha将不同 (3认同)

Sar*_*ran 9

对于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);
Run Code Online (Sandbox Code Playgroud)

在科特林

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)
Run Code Online (Sandbox Code Playgroud)

  • 这就是我一直在寻找的圣杯!多谢,伙计! (3认同)

小智 9

您可以使用 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)
Run Code Online (Sandbox Code Playgroud)


Kva*_*ant 7

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的代码,它对我来说效果很好。但是代码中有一个小错误:您正在使用所需半径的一半 - 我想您将半径作为直径,但半径已经是直径的一半,因此您应该将所有 /2 放在代码。 (2认同)

Yu-*_*hen 7

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Bha*_*gav 5

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

这些是使圆角矩形看起来整洁的前提条件

  • 边缘的半径必须等于(矩形的高度/ 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);
}
Run Code Online (Sandbox Code Playgroud)

现在,这将产生一个非常漂亮的圆角矩形,如下图所示在此处输入图片说明


Chr*_*aik 5

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

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

canvas.save();
canvas.clipRect(0, 0, 300, 300);
canvas.drawRoundRect(new RectF(0, 0, 300, 350), 50, 50, paint);
canvas.restore();
Run Code Online (Sandbox Code Playgroud)

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

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

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

mGradient.setBounds(0, 0, 300, 300);
mGradient.setCornerRadii(new int[] {50,50, 50,50, 0,0, 0,0});
Run Code Online (Sandbox Code Playgroud)

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


drs*_*boo 5

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

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

变量

private val path = Path()
private val paint = Paint()
Run Code Online (Sandbox Code Playgroud)

设置油漆

paint.color = Color.RED
paint.style = Paint.Style.FILL
Run Code Online (Sandbox Code Playgroud)

更改大小的更新路径

在不是 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)
Run Code Online (Sandbox Code Playgroud)

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

绘制路径

onDraw为视图或draw可绘制对象调用此方法。

canvas.drawPath(path, paint)
Run Code Online (Sandbox Code Playgroud)


Zel*_*mir 3

您可以使用中的drawLine()drawArc()函数逐段绘制该内容Canvas