Muchas apps deben poder controlar con precisión lo que se dibuja en la pantalla. Puede ser algo tan simple como colocar un cuadrado o un círculo en la pantalla en el lugar correcto, o podría ser una disposición compleja de elementos gráficos de muchos estilos diferentes.
Dibujo básico con modificadores y DrawScope
La forma principal de dibujar algo personalizado en Compose es con modificadores, como Modifier.drawWithContent
, Modifier.drawBehind
y Modifier.drawWithCache
.
Por ejemplo, para dibujar algo detrás de tu elemento componible, puedes usar el modificador drawBehind
para comenzar a ejecutar comandos de dibujo:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
Si todo lo que necesitas es un elemento componible que dibuje, puedes usar el elemento componible Canvas
. El elemento componible Canvas
es un wrapper conveniente alrededor de Modifier.drawBehind
. Coloca el Canvas
en tu diseño de la misma manera que lo harías con cualquier otro elemento de la IU de Compose. Dentro de Canvas
, puedes dibujar elementos con un control preciso sobre su estilo y ubicación.
Todos los modificadores de dibujo exponen un DrawScope
, un entorno de dibujo con alcance que mantiene su propio estado. Esto te permite establecer los parámetros para un grupo de elementos gráficos. DrawScope
proporciona varios campos útiles, como size
, un objeto Size
que especifica las dimensiones actuales de DrawScope
.
Para dibujar algo, puedes usar una de las muchas funciones de dibujo en DrawScope
. Por ejemplo, el siguiente código dibuja un rectángulo en la esquina superior izquierda de la pantalla:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }
![Rectángulo rosa dibujado sobre un fondo blanco que ocupa un cuarto de la pantalla](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_draw_rect.png?authuser=5&hl=es)
Para obtener más información sobre los diferentes modificadores de dibujo, consulta la documentación sobre modificadores de gráficos.
Sistema de coordenadas
Para dibujar algo en la pantalla, debes conocer el desplazamiento (x
y y
) y el tamaño del elemento. Con muchos de los métodos de dibujo en DrawScope
, la posición y el tamaño los proporcionan los valores de parámetros predeterminados. Los parámetros predeterminados suelen posicionar el elemento en el punto [0, 0]
sobre el lienzo y proporcionan un valor predeterminado de size
que cubre toda el área de dibujo, como en el ejemplo anterior. Puedes ver que el rectángulo se encuentra en la parte superior izquierda. Para ajustar el tamaño y la posición de tu elemento, debes comprender el sistema de coordenadas en Compose.
El origen del sistema de coordenadas ([0,0]
) se encuentra en el píxel superior izquierdo en el área de dibujo. x
aumenta a medida que se mueve hacia la derecha y y
aumenta a medida que se mueve hacia abajo.
![Cuadrícula que muestra el sistema de coordenadas, que muestra la parte superior izquierda [0, 0] y la parte inferior derecha [ancho, altura]](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_coordinate_system_drawing.png?authuser=5&hl=es)
Por ejemplo, si deseas dibujar una línea diagonal desde la esquina superior derecha del área del lienzo hasta la esquina inferior izquierda, puedes usar la función DrawScope.drawLine()
y especificar un desplazamiento inicial y final con las posiciones x e y correspondientes:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
Transformaciones básicas
DrawScope
ofrece transformaciones para cambiar dónde o cómo se ejecutan los comandos de dibujo.
Escala
Usa DrawScope.scale()
para aumentar el tamaño de las operaciones de dibujo por un factor. Las operaciones como scale()
se aplican a todas las operaciones de dibujo dentro de la expresión lambda correspondiente. Por ejemplo, el siguiente código aumenta el scaleX
10 veces y el scaleY
15 veces:
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }
![Un círculo escalado de manera no uniforme](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_scale.png?authuser=5&hl=es)
Trasladar
Usa DrawScope.translate()
para mover tus operaciones de dibujo hacia arriba, hacia abajo, hacia la izquierda o hacia la derecha. Por ejemplo, el siguiente código mueve el dibujo 100 px hacia la derecha y 300 px hacia arriba:
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }
![Un círculo que se movió fuera del centro](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_translate.png?authuser=5&hl=es)
Rotar
Usa DrawScope.rotate()
para rotar tus operaciones de dibujo alrededor de un punto de pivote. Por ejemplo, el siguiente código rota un rectángulo 45 grados:
Canvas(modifier = Modifier.fillMaxSize()) { rotate(degrees = 45F) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
![Un teléfono con un rectángulo rotado 45 grados en el centro de la pantalla](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_rotate.png?authuser=5&hl=es)
rotate()
para aplicar una rotación al alcance de dibujo actual, que rota el rectángulo 45 grados.
Inserción
Usa DrawScope.inset()
para ajustar los parámetros predeterminados del DrawScope
actual, cambiar los límites del dibujo y trasladar los dibujos según corresponda:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
Este código agrega relleno de forma efectiva a los comandos de dibujo:
![Un rectángulo que está relleno a su alrededor](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_inset.png?authuser=5&hl=es)
Transformaciones múltiples
Para aplicar varias transformaciones a tus dibujos, usa la función DrawScope.withTransform()
, que crea y aplica una sola transformación que combina todos los cambios deseados. El uso de withTransform()
es más eficiente que realizar llamadas anidadas a transformaciones individuales, ya que todas las transformaciones se realizan juntas en una sola operación, en lugar de que Compose deba calcular y guardar cada una de las transformaciones anidadas.
Por ejemplo, el siguiente código aplica una traslación y una rotación al rectángulo:
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
![Un teléfono con un rectángulo rotado hacia un lado de la pantalla](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_multiple_transforms.png?authuser=5&hl=es)
withTransform
para aplicar una rotación y una traslación, lo que rota el rectángulo y lo desplaza hacia la izquierda.Operaciones de dibujo comunes
Cómo dibujar texto
Para dibujar texto en Compose, generalmente puedes usar el elemento componible Text
. Sin embargo, si estás en un DrawScope
o quieres dibujar el texto de forma manual con personalización, puedes usar el método DrawScope.drawText()
.
Para dibujar texto, crea un TextMeasurer
usando rememberTextMeasurer
y llama a drawText
con el medidor:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
![Se muestra un saludo dibujado en Canvas](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_draw_text.png?authuser=5&hl=es)
Cómo medir texto
El diseño del texto funciona de manera diferente a otros comandos de dibujo. Normalmente, se asigna al comando de dibujo el tamaño (ancho y alto) para dibujar la forma o la imagen. Con el texto, hay algunos parámetros que controlan el tamaño del texto procesado, como el tamaño de la fuente, las fuentes, las ligaduras y el espacio entre las letras.
Con Compose, puedes usar un objeto TextMeasurer
para obtener acceso al tamaño medido del texto, según los factores anteriores. Si deseas dibujar un fondo detrás del texto, puedes usar la información medida para obtener el tamaño del área que ocupa el texto:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()), style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
Este fragmento de código genera un fondo rosa en el texto:
![Texto de varias líneas que ocupa 2⁄3 del área completa, con un rectángulo de fondo](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_draw_text_measured.png?authuser=5&hl=es)
Cuando se ajustan las restricciones, el tamaño de la fuente o cualquier propiedad que afecte el tamaño medido, la acción resulta en un tamaño nuevo. Puedes configurar un tamaño fijo para width
y height
, y el texto luego seguirá el conjunto TextOverflow
. Por ejemplo, el siguiente código renderiza texto en 1⁄3 de la altura y 1⁄3 del ancho del área componible, y establece TextOverflow
en TextOverflow.Ellipsis
:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixed( width = (size.width / 3f).toInt(), height = (size.height / 3f).toInt() ), overflow = TextOverflow.Ellipsis, style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
El texto ahora se dibuja en las restricciones con una elipsis al final:
![Texto dibujado sobre un fondo rosa con puntos suspensivos cortados.](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_draw_text_ellipsis.png?authuser=5&hl=es)
TextOverflow.Ellipsis
con restricciones fijas en la medición de textoCómo dibujar una imagen
Para dibujar un objeto ImageBitmap
con DrawScope
, carga la imagen con ImageBitmap.imageResource()
y, luego, llama a drawImage
:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
![Una imagen de un perro dibujado en Canvas](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_image.png?authuser=5&hl=es)
ImageBitmap
en CanvasCómo dibujar formas básicas
Hay muchas funciones de dibujo con formas en DrawScope
. Para dibujar una forma, usa una de las funciones de dibujo predefinidas, como drawCircle
:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
Resultado |
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
Cómo dibujar una ruta
Una ruta es una serie de instrucciones matemáticas que dan como resultado un dibujo una vez ejecutado. DrawScope
puede dibujar una ruta con el método DrawScope.drawPath()
.
Por ejemplo, supongamos que deseas dibujar un triángulo. Puedes generar una ruta con funciones como lineTo()
y moveTo()
usando el tamaño del área de trazado.
Luego, llama a drawPath()
con esta ruta recién creada para obtener un triángulo.
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )
![Triángulo con forma de ruta invertida de color púrpura dibujado en Compose](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_draw_path.png?authuser=5&hl=es)
Path
en ComposeCómo acceder al objeto Canvas
Con DrawScope
, no tienes acceso directo a un objeto Canvas
. Puedes usar DrawScope.drawIntoCanvas()
para obtener acceso al objeto Canvas
en el que puedes llamar a funciones.
Por ejemplo, si tienes un Drawable
personalizado que quieres dibujar en el lienzo, puedes acceder al lienzo, llamar a Drawable#draw()
y pasar el objeto Canvas
:
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )
![Un objeto ShapeDrawable negro ovalado que ocupa el tamaño original](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_graphics_canvas_draw_into_canvas.png?authuser=5&hl=es)
Drawable
Más información
Para obtener más información sobre cómo dibujar en Compose, consulta los siguientes recursos:
- Modificadores de gráficos: Obtén información sobre los diferentes tipos de modificadores de dibujo.
- Pincel: Obtén información sobre cómo personalizar la pintura de tu contenido.
- Diseños y gráficos personalizados en Compose - Android Dev Summit 2022: Obtén información para compilar una IU personalizada en Compose con diseños y gráficos.
- Ejemplo de JetLagged: Ejemplo de Compose que muestra cómo dibujar un gráfico personalizado.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Modificadores de gráficos
- Gráficos en Compose
- Líneas de alineación en Jetpack Compose