Personaliza una imagen

Puedes personalizar imágenes con propiedades en un elemento componible Image (contentScale, colorFilter). También puedes aplicar modificadores existentes para producir diferentes efectos en Image. Los modificadores se pueden usar en cualquier elemento componible, no solo en el elemento componible Image, mientras que contentScale y colorFilter son parámetros explícitos en este elemento componible Image.

Escala del contenido

Especifica una opción de contentScale para recortar o cambiar la forma en que se escala una imagen dentro de sus límites. De forma predeterminada, si no especificas una opción de contentScale, se usará ContentScale.Fit.

En el siguiente ejemplo, el elemento componible Image está restringido a un tamaño de 150 dp con un margen, y el fondo está configurado en amarillo en el elemento componible Image para mostrar las diferentes opciones de ContentScale en la siguiente tabla.

val imageModifier = Modifier
    .size(150.dp)
    .border(BorderStroke(1.dp, Color.Black))
    .background(Color.Yellow)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Fit,
    modifier = imageModifier
)

Si configuras diferentes opciones de ContentScale, se generarán distintos resultados. En la siguiente tabla, se te ayuda a elegir el modo de ContentScale correcto:

Imagen de origen La fuente del retrato, que muestra un perro. La fuente del paisaje, que muestra un perro diferente.
ContentScale Resultado - Imagen vertical: Resultado - Imagen horizontal:
ContentScale.Fit: Ajusta la imagen de manera uniforme y conserva la relación de aspecto (predeterminada). Si el contenido es más pequeño que el tamaño, la imagen se ampliará para que se ajuste a los límites. Un retrato de un perro escalado de manera uniforme. Un paisaje de perro escalado de manera uniforme.
ContentScale.Crop: Recorta la imagen en el centro del espacio disponible. Una imagen de retrato recortada para llenar un encuadre cuadrado, en la que solo se muestra la parte central de la imagen. Una imagen horizontal recortada para llenar un encuadre cuadrado, en la que solo se muestra la parte central de la imagen.
ContentScale.FillHeight: Escala la imagen de origen y mantiene la relación de aspecto para que los límites coincidan con la altura de destino. Una imagen de retrato ajustada para ocupar la altura de un marco cuadrado, en la que se muestra la imagen completa con un fondo amarillo visible a la izquierda y a la derecha. Una imagen horizontal ajustada para llenar la altura de un encuadre cuadrado, con los lados recortados.
ContentScale.FillWidth: Escala la imagen de origen y mantiene la relación de aspecto para que los límites coincidan con el ancho de destino. Una imagen vertical ajustada para ocupar el ancho de un marco cuadrado, con la parte superior y la inferior recortadas. Una imagen horizontal ajustada para ocupar el ancho de un marco cuadrado, en la que se muestra la imagen completa con el fondo amarillo visible en la parte superior e inferior.
ContentScale.FillBounds: Escala el contenido de forma vertical y horizontal, de manera no uniforme, para llenar los límites de destino. (Nota: Esto distorsionará las imágenes si las colocas en contenedores que no coincidan con la proporción exacta de la imagen). Imagen de retrato distorsionada para llenar por completo un marco cuadrado, lo que estira la imagen. Una imagen horizontal distorsionada para llenar por completo un encuadre cuadrado, lo que estira la imagen.
ContentScale.Inside: Escala la imagen de origen para mantener la relación de aspecto dentro de los límites de destino. Si la imagen de origen es menor o igual que la imagen de destino en ambas dimensiones, se comportará de manera similar a None. El contenido siempre se incluirá dentro de los límites. Si el contenido es más pequeño que los límites, no se aplicará ningún escalamiento. La imagen de origen es más grande que los límites: Una imagen vertical, originalmente más grande que sus límites cuadrados, se redujo para que quepa y, al mismo tiempo, se mantuvo la relación de aspecto, lo que muestra un fondo amarillo en los laterales. La imagen de origen es más pequeña que los límites: Una imagen de retrato, originalmente más pequeña que sus límites cuadrados, que se muestra en su tamaño original dentro del marco, con un fondo amarillo alrededor. La imagen de origen es más grande que los límites: Una imagen horizontal, originalmente más grande que sus límites cuadrados, se redujo para que quepa y, al mismo tiempo, se mantuvo la relación de aspecto, lo que muestra un fondo amarillo en la parte superior y la inferior. La imagen de origen es más pequeña que los límites: Una imagen horizontal, originalmente más pequeña que sus límites cuadrados, que se muestra en su tamaño original dentro del marco, con un fondo amarillo alrededor.
ContentScale.None: No aplica ningún escalamiento a la imagen de origen. Si el contenido es más pequeño que los límites de destino, no se ampliará para ajustarse al área. La imagen de origen es más grande que los límites: Una imagen de retrato, originalmente más grande que sus límites cuadrados, que se muestra en su tamaño original, con partes que se extienden más allá de la parte superior e inferior del marco. La imagen de origen es más pequeña que los límites: Una imagen de retrato, originalmente más pequeña que sus límites cuadrados, que se muestra en su tamaño original dentro del marco, con un fondo amarillo alrededor. La imagen de origen es más grande que los límites: Una imagen horizontal, originalmente más grande que sus límites cuadrados, que se muestra en su tamaño original, con partes que se extienden más allá de la izquierda y la derecha del encuadre. La imagen de origen es más pequeña que los límites: Una imagen horizontal, originalmente más pequeña que sus límites cuadrados, que se muestra en su tamaño original dentro del marco, con un fondo amarillo alrededor.

Cómo recortar un elemento Image componible en una forma

Para que una imagen se ajuste a una forma, usa el modificador clip integrado. Para recortar una imagen con forma circular, usa Modifier.clip(CircleShape):

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(CircleShape)
)

Imagen de un perro recortada en un círculo perfecto.
Figura 1: Recorte de una imagen con CircleShape.

Para una forma con esquinas redondeadas, usa Modifier.clip(RoundedCornerShape(16.dp) con el tamaño de las esquinas que deseas redondear:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(RoundedCornerShape(16.dp))
)

Imagen cuadrada de un perro recortada con esquinas redondeadas.
Figura 2: Recorte de una imagen con RoundedCornerShape.

También puedes crear tu propia forma de recorte si extiendes Shape y proporcionas un Path para que se recorte la forma:

class SquashedOval : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val path = Path().apply {
            // We create an Oval that starts at ¼ of the width, and ends at ¾ of the width of the container.
            addOval(
                Rect(
                    left = size.width / 4f,
                    top = 0f,
                    right = size.width * 3 / 4f,
                    bottom = size.height
                )
            )
        }
        return Outline.Generic(path = path)
    }
}

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(SquashedOval())
)

Una imagen cuadrada de un perro recortada en una forma ovalada personalizada.
Figura 3: Recorte de una imagen con una forma de ruta personalizada.

Cómo agregar un borde a un elemento Image componible

Una operación frecuente es combinar Modifier.border() con Modifier.clip() para crear un borde alrededor de una imagen:

val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, Color.Yellow),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

Imagen cuadrada de un perro recortada en un círculo, con un borde amarillo alrededor de la forma circular.
Figura 4: Una imagen recortada con un borde alrededor.

Para crear un borde degradado, puedes usar la API de Brush para dibujar un borde degradado de arcoíris alrededor de la imagen:

val rainbowColorsBrush = remember {
    Brush.sweepGradient(
        listOf(
            Color(0xFF9575CD),
            Color(0xFFBA68C8),
            Color(0xFFE57373),
            Color(0xFFFFB74D),
            Color(0xFFFFF176),
            Color(0xFFAED581),
            Color(0xFF4DD0E1),
            Color(0xFF9575CD)
        )
    )
}
val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, rainbowColorsBrush),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

Imagen circular de un perro con un borde de gradiente de arcoíris alrededor de la forma circular.
Figura 5: Marco circular gradiente de arcoíris.

Cómo establecer una relación de aspecto personalizada

Para transformar una imagen en una relación de aspecto personalizada, usa Modifier.aspectRatio(16f/9f) para proporcionar una relación de aspecto personalizada para una imagen (o cualquier elemento componible).

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    modifier = Modifier.aspectRatio(16f / 9f)
)

Una imagen cuadrada de un perro, transformada a una relación de aspecto de 16:9, lo que la hace más ancha y más corta.
Figura 6: Usa Modifier.aspectRatio(16f/9f) en un objeto Image.

Filtro de color: cómo transformar los colores de píxeles de la imagen

El elemento Image componible tiene un parámetro colorFilter que puede cambiar el resultado de píxeles individuales de la imagen.

Cómo teñir imágenes

Si usas ColorFilter.tint(color, blendMode), se aplicará un modo de combinación con el color determinado en el elemento componible Image. ColorFilter.tint(color, blendMode) usa BlendMode.SrcIn para ajustar el tono del contenido, lo que significa que se mostrará el color proporcionado donde se mostrará la imagen en pantalla. Es útil para íconos y vectores que necesitan un tema diferente.

Image(
    painter = painterResource(id = R.drawable.baseline_directions_bus_24),
    contentDescription = stringResource(id = R.string.bus_content_description),
    colorFilter = ColorFilter.tint(Color.Yellow)
)

Imagen de un autobús con un tinte amarillo aplicado.
Figura 7: ColorFilter.tint se aplicó con BlendMode.SrcIn.

Otros BlendMode tienen diferentes efectos. Por ejemplo, configurar BlendMode.Darken con un Color.Green en una imagen produce el siguiente resultado:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.tint(Color.Green, blendMode = BlendMode.Darken)
)

Un perro con un tono verde aplicado con BlendMode.Darken, lo que genera tonos verdes más oscuros.
Figura 8: Color.Green tint con BlendMode.Darken.

Consulta la documentación de referencia de BlendMode para obtener más información sobre los diferentes modos de combinación disponibles.

Cómo aplicar un filtro Image con una matriz de colores

Transforma tu imagen con la opción de matriz de colores ColorFilter. Por ejemplo, para aplicar un filtro blanco y negro a tus imágenes, puedes usar ColorMatrix y configurar la saturación en 0f.

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
)

Un perro con un filtro en blanco y negro aplicado, que quita toda la saturación de color.
Figura 9: Matriz de colores con saturación 0 (imagen en blanco y negro).

Cómo ajustar el contraste o el brillo de un elemento Image componible

Para cambiar el contraste y el brillo de una imagen, puedes usar ColorMatrix a fin de cambiar los valores:

val contrast = 2f // 0f..10f (1 should be default)
val brightness = -180f // -255f..255f (0 should be default)
val colorMatrix = floatArrayOf(
    contrast, 0f, 0f, 0f, brightness,
    0f, contrast, 0f, 0f, brightness,
    0f, 0f, contrast, 0f, brightness,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

Un perro con mayor brillo y contraste, lo que lo hace parecer más vívido.
Figura 10: Brillo y contraste de la imagen ajustados con ColorMatrix.

Cómo invertir los colores de un elemento Image componible

Para invertir los colores de una imagen, configura ColorMatrix:

val colorMatrix = floatArrayOf(
    -1f, 0f, 0f, 0f, 255f,
    0f, -1f, 0f, 0f, 255f,
    0f, 0f, -1f, 0f, 255f,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

Un perro con los colores invertidos, que muestra un efecto similar al negativo.
Figura 11: Colores invertidos en la imagen.

Cómo desenfocar un elemento Image componible

Para desenfocar una imagen, usa Modifier.blur() y proporciona radiusX y radiusY, que especifican el radio de desenfoque en las direcciones horizontal y vertical, respectivamente.

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
        )
)

Un perro con un fuerte efecto de desenfoque aplicado, lo que lo hace parecer indistinto y desenfocado.
Figura 12: BlurEffect aplicada a una imagen.

Cuando se desenfoca Images, se recomienda usar BlurredEdgeTreatment(Shape), en lugar de BlurredEdgeTreatment.Unbounded, ya que este último se usa para desenfocar renderizaciones arbitrarias que se espera que se rendericen fuera de los límites del contenido original. En el caso de las imágenes, es probable que no se rendericen fuera de los límites del contenido, mientras que desenfocar un rectángulo redondeado puede requerir esta distinción.

Por ejemplo, si configuramos BlurredEdgeTreatment como Unbounded en la imagen anterior, los bordes de esta aparecerán desenfocados en lugar de nítidos:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment.Unbounded
        )
        .clip(RoundedCornerShape(8.dp))
)

Una imagen borrosa de un perro, en la que el desenfoque se extiende más allá de los límites de la imagen original, lo que hace que los bordes se vean difusos.
Figura 13: BlurEdgeTreatment.Unbounded.