Я нашел функцию для прямоугольников, в которой все 4 угла закруглены, но я хочу, чтобы только 2 верхних угла были круглыми. Что я могу сделать?
canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
Я нашел функцию для прямоугольников, в которой все 4 угла закруглены, но я хочу, чтобы только 2 верхних угла были круглыми. Что я могу сделать?
canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
Вы можете нарисовать этот фрагмент за фрагментом, используя функции drawLine()
и drawArc()
из файла Canvas
.
Используйте путь. Преимущество заключается в том, что он работает с API менее 21 (Arc также ограничен таким образом, поэтому я квад). Это проблема, потому что не у всех есть 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 для конформтооригиналпост. Просто линия до контрольной точки там.
Если вы хотите сделать все это, но не заботитесь о вещах, предшествующих 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;
}
Таким образом, конформтооригиналпост фактически рисует закругленный прямоугольник без округления двух нижних битов.
Я бы нарисовал два прямоугольника:
canvas.drawRect(new RectF(0, 110, 100, 290), paint);
canvas.drawRoundRect(new RectF(0, 100, 100, 200), 6, 6, paint);
Или что-то в этом роде, вы просто накладываете их друг на друга, чтобы верхние углы были круглыми. Предпочтительно, вы должны написать метод для этого
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
, который доступен из API 21.
- person Mokkun; 01.05.2017
paint.setColor(Color.argb(200, 0,0,0));
установит альфу для краски.
- person niek tuytel; 28.11.2020
Для 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)
Я изменил этот ответ, чтобы вы могли указать, какой угол вы хотите сделать круглым, а какой — острым. также работает на pre-lolipop
Usage Example:только верхний правый и нижний правый углы закруглены
Path path = RoundedRect(0, 0, fwidth , fheight , 5,5,
false, true, true, false);
canvas.drawPath(path,myPaint);
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;
}
Простая вспомогательная функция, написанная на 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)
вы можете легко добиться этого, используя 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)
Я достиг этого, выполнив следующие шаги.
Это необходимые условия для того, чтобы прямоугольник со скругленными углами выглядел аккуратно.
Далее идут шаги по рисованию прямоугольника со скругленными углами.
Сначала рисуем по 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);
}
Теперь это приводит к действительно красивому прямоугольнику со скругленными углами, как показано ниже
Один из простых и эффективных способов нарисовать сплошную сторону — это использовать отсечение — прямоугольное отсечение практически бесплатно и требует гораздо меньше кода для написания, чем пользовательский путь.
Если мне нужен прямоугольник размером 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() , вы можете задать любому углу любую округлость и разумно анимировать между состояниями.
Это старый вопрос, однако я хотел добавить свое решение, потому что оно использует собственный SDK без большого количества пользовательского кода или хакерского рисования. Это решение поддерживается вплоть до API 1.
Способ сделать это правильно - создать путь (как упоминалось в других ответах), однако предыдущие ответы, похоже, упускают из виду вызов функции addRoundedRect
, который принимает радиусы для каждого угла.
Переменные
private val path = Path()
private val paint = 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)
путь Версия #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
}
}
нарисовать круглый прямоугольник с левыми закругленными углами
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);
}
Лучше использовать 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)
Вот мой ответ на поставленный выше вопрос. Здесь я создал функцию расширения Kotlin, которая использует Path
вместе с функцией quadTo
, которую также можно использовать в 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)
}
Мы можем вызвать функцию с объектом холста и передать RectF
размер, к которому мы хотим применить кривую.
Кроме того, мы можем передать логическое значение для углов, которые мы хотим скруглить. Этот ответ можно дополнительно настроить, чтобы принять радиус для отдельных углов.