Я нашел функцию для прямоугольников со всеми 4 углами вокруг, но я хочу иметь только два верхних угла. Что я могу сделать?
canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
Я нашел функцию для прямоугольников со всеми 4 углами вокруг, но я хочу иметь только два верхних угла. Что я могу сделать?
canvas.drawRoundRect(new RectF(0, 100, 100, 300), 6, 6, paint);
Вы можете нарисовать эту часть по частям с помощью функций drawLine()
и drawArc()
из Canvas
.
Используйте путь. Это имеет преимущество, заключающееся в том, что он работает для 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 фактически рисует закругленный прямоугольник без нижних двух бит, закругленных.
Я бы нарисовал два прямоугольника:
canvas.drawRect(new RectF(0, 110, 100, 290), paint);
canvas.drawRoundRect(new RectF(0, 100, 100, 200), 6, 6, paint);
Или что-то в этом роде, вы просто перекрываете их, чтобы верхние углы были круглыми. Предпочтительно вам следует написать метод для этого
Я изменил этот ответ, чтобы вы могли указать, в каком углу вы хотите быть круглым и какой из них вы хотите быть острым. также работает на pre-lolipop
Пример использования:только верхние правые и углы botton-right закруглены
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;
}
Простая вспомогательная функция, написанная в Котлине.
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)
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;
}
Для 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)
Я достиг этого, выполнив следующие шаги.
Это предварительные условия для того, чтобы прямоугольник со скругленными углами выглядел аккуратно
Далее идут шаги для рисования скругленного прямоугольника.
Сначала нарисуем 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 смежных углах, поэтому он немного менее настраивается, чем подход на основе 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() вы можете установить любой угол в любую округленность и разумно анимировать между состояниями.
нарисуйте прямоугольник с закругленными углами слева
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)
Это старый вопрос, однако я хотел добавить свое решение, потому что оно использует собственный 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)