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

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


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 RectF(0, 100, 100, 300), 6, 6, paint);

9 个解决方案

#1


3  

You can draw that piece by piece using drawLine() and drawArc() functions from the Canvas.

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

#2


38  

I would draw two rectangles:

我会画两个矩形:

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

Or something like that, you just overlap them so that the upper corners will be round. Preferably you should write a method for this

或类似的东西,你只是重叠它们,以便上角是圆的。最好你应该为此编写一种方法

#3


33  

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

使用路径。它具有为小于21的API工作的优点(因此,Arc也是有限的,这就是我四倍的原因)。这是一个问题,因为并非每个人都有棒棒糖。但是,您可以指定一个RectF并使用它设置值并使用arc返回API 1,但是您不会使用静态(不声明新对象来构建对象)。

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.

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

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.

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

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

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

arcquadimage

#4


19  

I changed this answer so you can set which corner you want to be round and which one you want to be sharp. also works on pre-lolipop

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

Usage Example:

only top-right and botton-right corners are rounded

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

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

#5


7  

Simple helper function written in Kotlin.

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

Usage:

用法:

canvas.drawTopRoundRect(rect, paint, radius)

#6


5  

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

#7


1  

draw round rect with left rounded corners

绘制圆形矩形与左圆角

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

#8


1  

I achieved this by following the below steps.

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

These are the pre-requisites for the rounded rectangle to look neat

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

  • The radius of the edges have to be equal to the (height of the rectangle / 2). This is because if its any different value then the place where the curve meets straight line of the rectangle will not be
  • 边缘的半径必须等于(矩形的高度/ 2)。这是因为如果它有任何不同的值,那么曲线与矩形的直线相交的位置将不会

Next is the steps to draw the rounded rectangle.

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

  • First we draw 2 circles on the left and right side, with the radius = height of rectange / 2

    首先,我们在左侧和右侧绘制2个圆,半径= rectange / 2的高度

  • Then we draw a rectangle between these circles to get the desired rounded rectangle.

    然后我们在这些圆之间绘制一个矩形以获得所需的圆角矩形。

I am posting the code below

我发布下面的代码

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

Now this results in a really nice rounded rectangle like the one shown belowenter image description here

现在这会产生一个非常漂亮的圆角矩形,如下图所示

#9


1  

One simple and efficient way to draw a solid side is to use clipping - rect clipping is essentially free, and a lot less code to write than a custom Path.

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

If I want a 300x300 rect, with the top left and right rounded by 50 pixels, you can do:

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

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

This approach will only work for rounding on 2 or 3 adjacent corners, so it's a little less configurable than a Path based approach, but using round rects is more efficient, since drawRoundRect() is fully hardware accelerated (that is, tessellated into triangles) while drawPath() always falls back to software rendering (software-draw a path bitmap, and upload that to be cached on the GPU).

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

Not a huge performance issue for small infrequent drawing, but if you're animating paths, the cost of software draw can make your frame times longer, and increase your chance to drop frames. The path mask also costs memory.

对于不常见的小型绘图而言,这不是一个巨大的性能问题,但如果你是动画路径,软件绘制的成本可以使你的帧时间更长,并增加丢帧的机会。路径掩码也会占用内存。

If you do want to go with a Path-based approach, I'd recommend using GradientDrawable to simplify the lines of code (assuming you don't need to set a custom shader, e.g. to draw a Bitmap).

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

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

With GradientDrawable#setCornerRadii(), you can set any corner to be any roundedness, and reasonably animate between states.

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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2018 ITdaan.com 粤ICP备14056181号