El API de Canvas nos facilita con tres funciones independientes efectuar las transformaciones de rotación, escalado y translación.
Dibujar en la parte superior un rectángulo sin aplicar transformaciones, luego dibujar el mismo rectángulo en medio de la pantalla y aplicar una rotación de 45 grados. En la parte inferior de pantalla mostrar otro rectángulo del mismo tamaño pero aplicarle un escalado que lo muestre con un tamaño del 50% con respecto al original.
Finalmente dibujar un círculo con centro en la coordenada (0,0) y radio de 50, seguidamente aplicar una translación de 50 píxeles a la derecha y 50 píxeles hacia abajo.
Crearemos un proyecto llamado: 'Compose30'
La interfaz visual a implementar debe ser similar a:
El código a implementar en Kotlin para obtener dicha funcionalidad es:
package com.tutorialesprogramacionya.compose30 import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.graphics.drawscope.translate class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PantallaPrincipal() } } } @Composable fun PantallaPrincipal() { Canvas(modifier = Modifier.fillMaxSize(), onDraw = { val ancho = size.width val alto = size.height drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, 0f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Red ) rotate(degrees = 45f) { drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, alto / 2 - alto * 0.10f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Green ) } scale(scale = 0.5f) { drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, alto - alto * 0.10f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Blue ) } translate(left=50f,top = 50f) { drawCircle( center = Offset(x = 0f, y = 0f), radius = 50f, color=Color.Cyan ) } }) }
El primer rectángulo no se le aplican transformaciones, lo ubicamos en medio de la pantalla y ocupa un ancho y alto de 20% (damos dichos valores llamando a la función size):
@Composable fun PantallaPrincipal() { Canvas(modifier = Modifier.fillMaxSize(), onDraw = { val ancho = size.width val alto = size.height drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, 0f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Red )
Llamamos a la función rotate y le pasamos en el parámetro degrees el valor 45f, luego en la función lambda que le pasamos a rotate graficamos un rectángulo, igual que el anterior. Veremos que el mismo aparece rotado 45 grados:
rotate(degrees = 45f) { drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, alto / 2 - alto * 0.10f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Green ) }
Al rectángulo de la parte inferior de la pantalla le aplicamos un escalado de 0.5 (valor mayor a 1 obtenemos una imagen más grande):
scale(scale = 0.5f) { drawRect( topLeft = Offset(ancho / 2 - ancho * 0.10f, alto - alto * 0.10f), size = Size(ancho * 0.20f, alto * 0.20f), color = Color.Blue ) }
El círculo para evitar que quede parte fuera de la pantalla le aplicamos una translación de 50 píxeles en x e y:
translate(left=50f,top = 50f) { drawCircle( center = Offset(x = 0f, y = 0f), radius = 50f, color=Color.Cyan ) }
Este proyecto lo puede descargar en un zip desde este enlace: Compose30.zip
Podemos hacer varias transformaciones sucesivas:
@Composable fun PantallaPrincipal() { val imagen=ImageBitmap.imageResource(id = R.drawable.fondo) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { val ancho = size.width val alto = size.height rotate(degrees = 45f) { scale(scale=0.6f) { drawImage( image = imagen, dstOffset = IntOffset(0, 0), dstSize = IntSize(ancho.toInt(), alto.toInt()) ) } } }) }
En el ejemplo estamos escalando la imagen que ocupa toda la pantalla y rotando en 45 grados.
Teniendo como resultado:
Hay una solución más eficiente cuando tenemos que hacer transformaciones sucesivas. Debemos llamar a la función withTransform y pasar en la primera función lambda las transformaciones y en la segunda función lambda las funciones a las que se debe aplicar dichas transformaciones:
@Composable fun PantallaPrincipal() { val imagen=ImageBitmap.imageResource(id = R.drawable.fondo) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { val ancho = size.width val alto = size.height withTransform({ rotate(degrees = 45f) scale(scale = 0.6f) }) { drawImage( image = imagen, dstOffset = IntOffset(0, 0), dstSize = IntSize(ancho.toInt(), alto.toInt()) ) } }) }