Как использовать андроид холст, чтобы нарисовать прямоугольник только с верхним и верхним углами? - PullRequest
43 голосов
/ 05 мая 2011

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

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

Ответы [ 11 ]

49 голосов
/ 22 февраля 2015

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

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

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

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

arcquadimage

41 голосов
/ 05 мая 2011

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

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

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

24 голосов
/ 27 февраля 2016

Я изменил этот ответ, чтобы вы могли установить, какой угол вы хотите сделать круглым, а какой - четким.также работает на 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;
    }
9 голосов
/ 05 ноября 2017

Простая вспомогательная функция написана на 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)
6 голосов
/ 30 июня 2016
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 голосов
/ 21 мая 2019

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

в Kotlin

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)
5 голосов
/ 13 мая 2016

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

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

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

4 голосов
/ 22 августа 2017

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

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

2 голосов
/ 20 июля 2015

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

  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);
    }
2 голосов
/ 05 мая 2011

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

...