Подтвердить что ты не робот

Как использовать андроидный холст, чтобы нарисовать прямоугольник с круговыми углами только на вершине и вправо?

Я нашел функцию для прямоугольников со всеми 4 углами вокруг, но я хочу иметь только два верхних угла. Что я могу сделать?

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

Ответ 1

Вы можете нарисовать эту часть по частям с помощью функций drawLine() и drawArc() из Canvas.

Ответ 2

Используйте путь. Это имеет преимущество, заключающееся в том, что он работает для API менее 21 (поэтому Arc также ограничен, поэтому я quad). Это проблема, потому что не все имеют Lollipop. Тем не менее, вы можете указать 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;
}

Вы хотите проложить все пути до этих угловых бит, а не через квадрат. Это то, что подходит для соответствия TOOriginalPost. Просто подключитесь к контрольной точке.

Если вы хотите сделать это все, но не заботитесь о материалах, предшествующих 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;
}

Итак, КонтекстToOriginalPost фактически рисует закругленный прямоугольник без нижних двух бит, закругленных.

arcquadimage

Ответ 3

Я бы нарисовал два прямоугольника:

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

Или что-то в этом роде, вы просто перекрываете их, чтобы верхние углы были круглыми. Предпочтительно вам следует написать метод для этого

Ответ 4

Я изменил этот ответ, чтобы вы могли указать, в каком углу вы хотите быть круглым и какой из них вы хотите быть острым. также работает на pre-lolipop

Пример использования:

только верхние правые и углы botton-right закруглены

 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

Простая вспомогательная функция, написанная в Котлине.

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)

Ответ 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;
}

Ответ 7

Для 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)

Ответ 8

Я достиг этого, выполнив следующие шаги.

Это предварительные условия для того, чтобы прямоугольник со скругленными углами выглядел аккуратно

  • Радиус ребер должен быть равен (высота прямоугольника /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);
}

Теперь это приводит к действительно хорошему прямоугольнику с закругленными углами, как показано ниже enter image description here

Ответ 9

Один простой и эффективный способ сделать сплошную сторону - использовать отсечение - прямое обрезание по существу бесплатное и гораздо меньше кода для записи, чем для обычного пути.

Если я хочу прямоугольник 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 смежных углах, поэтому он немного менее настраивается, чем подход на основе Path, но использование округлых прямоугольников является более эффективным, так как drawRoundRect() полностью аппаратно ускорен (то есть тесселлированный в треугольники), в то время как drawPath() всегда возвращается к рендерингу программного обеспечения (программно-рисовать растровое изображение пути и загружать его для кеширования на графическом процессоре).

Не большая проблема с производительностью для небольшого нечастого рисования, но если вы анимируете пути, стоимость рисования программного обеспечения может увеличить время вашего кадра и увеличить шанс сброса кадров. Маска пути также стоит памяти.

Если вы хотите использовать подход на основе Path, я бы рекомендовал использовать GradientDrawable для упрощения строк кода (при условии, что вам не нужно устанавливать пользовательский шейдер, например, для рисования растрового изображения).

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

С помощью GradientDrawable # setCornerRadii() вы можете установить любой угол в любую округленность и разумно анимировать между состояниями.

Ответ 10

нарисуйте прямоугольник с закругленными углами слева

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

Ответ 11

Использовать 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)

Ответ 12

Это старый вопрос, однако я хотел добавить свое решение, потому что оно использует собственный 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)

Этот код создает прямоangularьник с закругленными углами 100x100 с верхними углами, закругленными с радиусом 25.

Нарисовать путь

Назовите это в onDraw для представления или draw для рисования.

canvas.drawPath(path, paint)