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

时间:2011-05-05 10:29:18

标签: android android-canvas draw

我找到了一个矩形函数,所有四个角都是圆的,但我想只有前两个角。我该怎么办?

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

16 个答案:

答案 0 :(得分:47)

使用路径。它具有为小于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的内容。只需到那里的控制点。

如果你想这样做但不关心前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;
}

所以, conformToOriginalPost实际绘制一个圆角矩形,而底部的两位没有圆角。

arcquadimage

答案 1 :(得分:41)

我会绘制两个矩形:

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

或类似的东西,你只是重叠它们,以便上角是圆的。最好你应该为这个

编写一个方法

答案 2 :(得分:23)

我更改了this回答,因此您可以设置要转弯的哪个角落以及您想要哪个角落。也适用于pre-lolipop

使用示例

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

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

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

答案 3 :(得分:9)

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

答案 4 :(得分:6)

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

答案 5 :(得分:4)

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

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

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

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

  • 首先我们在左侧和右侧绘制2个圆圈,半径= rectange的高度/ 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);
}

现在这会产生一个非常漂亮的圆角矩形,如下面enter image description here

所示

答案 6 :(得分:4)

一种简单有效的方法是使用剪切 - 直接剪切基本上是免费的,并且编写的代码比自定义路径少得多。

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

function showClick() {
    this.clicks = 0; // this === window here
    return function(e) {
        clicks++
        console.log("click "+clicks+" at "+[e.clientX,e.clientY])
        return clicks
    }
}
document.getElementById("b1").onclick=showClick();
document.getElementById("b2").onclick=showClick();

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

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

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

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

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

答案 7 :(得分:4)

有一个Path.addRoundRect(RectF rect, float[] radii, Path.Direction dir)重载,它使用八个值的float数组,其中我们可以为四个角中的每个角指定x半径和y半径。这些值是[x,y]对,从左上角开始,然后沿顺时针方向旋转其余部分。对于我们要舍入的那些角,我们将该对的两个值都设置为半径值,对于不想要的角,将其保留为零。

答案 8 :(得分:2)

您可以使用drawLine()中的drawArc()Canvas函数逐一绘制。

答案 9 :(得分: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);
    }

答案 10 :(得分:2)

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

floats = 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)

答案 11 :(得分:2)

这是一个古老的问题,但是我想添加我的解决方案,因为它使用的是本机SDK,而没有大量自定义代码或骇人的绘图。从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)

答案 12 :(得分:1)

使用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)

答案 13 :(得分:1)

这是我对上述问题的回答。在这里,我创建了使用PathquadTo函数的Kotlin扩展函数,该函数也可以在较低级别的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传递要在其上应用曲线的尺寸。

此外,我们可以将布尔值传递给要舍入的角。 可以进一步定制该答案,以接受各个角的半径。

答案 14 :(得分:0)

您可以使用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)

答案 15 :(得分:0)

一种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
    }
}

Two rounded corners Three rounded corners Three rounded corners