with studio finger example advanced android android-canvas draw

studio - layout canvas android



¿Cómo usar el lienzo de Android para dibujar un rectángulo con solo las esquinas redondeadas hacia arriba y hacia atrás? (9)

Cambié this respuesta para que puedas establecer qué esquina quieres que sea redonda y cuál quieres que sea nítida. también funciona en pre-lolipop

Ejemplo de uso :

solo las esquinas superior derecha e inferior derecha son redondeadas

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

Encontré una función para rectángulos con las 4 esquinas redondeadas, pero quiero tener las dos esquinas superiores redondeadas. ¿Que puedo hacer?

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


Dibujaría dos rectángulos:

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

O algo así, simplemente superponlos para que las esquinas superiores sean redondas. Preferiblemente deberías escribir un método para esto


Función auxiliar simple escrita en 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 ) }

Uso:

canvas.drawTopRoundRect(rect, paint, radius)


Lo logré siguiendo los pasos a continuación.

Estos son los requisitos previos para que el rectángulo redondeado se vea limpio

  • El radio de los bordes debe ser igual a (altura del rectángulo / 2). Esto es porque si tiene un valor diferente, entonces el lugar donde la curva se encuentra con la línea recta del rectángulo no será

A continuación se muestran los pasos para dibujar el rectángulo redondeado.

  • Primero dibujamos 2 círculos en el lado izquierdo y derecho, con el radio = altura de rectange / 2

  • Luego dibujamos un rectángulo entre estos círculos para obtener el rectángulo redondeado deseado.

Estoy publicando el código a continuación

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

Ahora esto da como resultado un rectángulo redondeado realmente agradable como el que se muestra a continuación


Puede dibujar esa pieza por pieza utilizando las drawLine() y drawArc() desde el Canvas .


Una manera simple y eficiente de dibujar un lado sólido es utilizar el recorte: el recorte recto es esencialmente libre y mucho menos código para escribir que un Camino personalizado.

Si quiero un rect 300x300, con la parte superior izquierda y derecha redondeada por 50 píxeles, puede hacer:

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

Este enfoque solo funcionará para redondear en 2 o 3 esquinas adyacentes, por lo que es un poco menos configurable que un enfoque basado en Ruta, pero usar rectas redondas es más eficiente, ya que drawRoundRect () es completamente acelerado por hardware (es decir, teselado en triángulos) mientras que drawPath () siempre recurre a la representación de software (software: dibuja un mapa de bits de ruta y lo carga en la memoria caché de la GPU).

No es un gran problema de rendimiento para pequeños dibujos poco frecuentes, pero si estás animando rutas, el costo del sorteo de software puede hacer que tus cuadros sean más largos y aumentar tus posibilidades de soltar marcos. La máscara de ruta también cuesta memoria.

Si quieres seguir un enfoque basado en Path, te recomiendo usar GradientDrawable para simplificar las líneas de código (suponiendo que no necesites establecer un shader personalizado, por ejemplo, para dibujar un mapa de bits).

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

Con GradientDrawable#setCornerRadii() , puede configurar cualquier esquina para redondear y animar razonablemente entre estados.


Usa un camino. Tiene la ventaja de trabajar para API de menos de 21 (Arc también está limitado por lo tanto, por eso I quad). Lo cual es un problema porque todavía no todo el mundo tiene Lollipop. Sin embargo, puede especificar un RectF y establecer los valores con eso y usar el arco de regreso a la API 1, pero luego no podrá usar un estático (sin declarar un nuevo objeto para construir el objeto).

Dibujando un rect redondo:

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

Como una función completa:

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

Te gustaría alinear todo el camino hasta esas partes de la esquina, en lugar de cuadrar sobre ellas. Esto es lo que hace el ajuste verdadero para conformToOriginalPost. Solo línea al punto de control allí.

Si quiere hacer todo eso pero no le importan las cosas anteriores a Lollipop, e insista con urgencia en que si su rx y ry son lo suficientemente altas, debería dibujar un círculo.

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

Por lo tanto, conformToOriginalPost realmente dibuja un rectángulo redondeado sin los dos bits inferiores redondeados.


dibujar rect redondo con esquinas redondeadas a la izquierda

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


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