Cómo personalizar una imagen

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Las imágenes se pueden personalizar mediante propiedades en un elemento Image que admite composición (contentScale, colorFilter). También puedes aplicar el Modifiers existente para producir diferentes efectos en Image. Los modificadores se pueden usar en cualquier elemento que admite composición, no solo en el elemento Image que admite composición, mientras que contentScale y colorFilter son parámetros explícitos en este elemento Image.

ContentScale

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 Image que admite composición está restringido a un tamaño de 150 dp con un margen, y el fondo está configurado en amarillo en el elemento Image que admite composición 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. A continuación, se muestra una tabla que puede ayudarte a elegir el modo correcto de ContentScale que necesitas:

Imagen de origen Imagen de origen vertical Imagen de origen horizontal
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. ContentScale.Fit vertical ContentScale.Fit horizontal
ContentScale.Crop: Recorta la imagen en el centro del espacio disponible. ContentScale.Crop vertical ContentScale.Crop horizontal
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. ContentScale.FillHeight vertical ContentScale.FillHeight horizontal
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. ContentScale.FillWidth vertical ContentScale.FillWidth horizontal
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). ContentScale.FillBounds vertical ContentScale.FillBounds horizontal
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 estará 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: ContentScale.Inside vertical, la imagen de origen es más grande que los límites La imagen de origen es más pequeña que los límites: ContentScale.Inside vertical, la imagen de origen es más pequeña que los límites La imagen de origen es más grande que los límites: ContentScale.Inside horizontal, la imagen de origen es más grande que los límites La imagen de origen es más pequeña que los límites: ContentScale.Inside horizontal, la imagen de origen es más pequeña que los límites
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: ContentScale.None vertical, la imagen de origen es más grande que los límites La imagen de origen es más pequeña que los límites: ContentScale.None vertical, la imagen de origen es más pequeña que los límites La imagen de origen es más grande que los límites: ContentScale.None horizontal, la imagen de origen es más grande que los límites La imagen de origen es más pequeña que los límites: ContentScale.None horizontal, la imagen de origen es más pequeña que los límites

Cómo recortar una imagen que admite composición 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)
)
Recorte de una imagen con CircleShape
Figura 1: Recorte de una imagen con CircleShape

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))
)
Recorte de una imagen con RoundedCornerShape
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 = "Golden retriever in falling autumn leaves",
    contentScale = ContentScale.Crop,
    modifier = Modifier.size(200.dp).clip(SquashedOval())
)
Recorte de una imagen con una forma de ruta personalizada
Figura 3: Recorte de una imagen con una forma de ruta personalizada

Cómo agregar un margen a un elemento Image que admite composición

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)
)
Recorta una imagen y proporciona un margen alrededor
Figura 4: Recorta una imagen y proporciona un margen alrededor

Si deseas crear un marco gradiente, puedes usar la API de Brush para dibujar un marco gradiente 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)
)
Marco circular gradiente de arcoíris
Figura 5: Marco circular gradiente de arcoíris

Cómo establecer una relación de aspecto personalizada

Con el objeto de transformar una imagen en una relación de aspecto personalizada, usa Modifier.aspectRatio(16f/9f) a fin de proporcionar una relación de aspecto personalizada para una imagen (o cualquier elemento que admita composición).

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    modifier = Modifier.aspectRatio(16f/9f)
)
Uso de Modifier.aspectRatio(16f/9f) en la imagen
Figura 6: Uso de Modifier.aspectRatio(16f/9f) en la imagen

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

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

Cómo ajustar el tono de una imagen

Si usas ColorFilter.tint(color, blendMode), se aplicará un modo de combinación con el color determinado en el elemento Image que admite composición. 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.ic_bus_24),
    contentDescription = stringResource(id = R.string.bus_schedule_content_desc),
    colorFilter = ColorFilter.tint(Color.Yellow)
)
ColorFilter.tint aplicado con BlendMode.SrcIn
Figura 7: ColorFilter.tint aplicado con BlendMode.SrcIn

Otros resultados de 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_desc),
    colorFilter = ColorFilter.tint(Color.Green, blendMode = BlendMode.Darken)
)
Tono Color.Green con BlendMode.Darken
Figura 8: Tono Color.Green 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 de 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_desc),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
)
Matriz de colores con saturación 0 (imagen en blanco y negro)
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 que admite composición

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_desc),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)
Brillo y contraste de la imagen ajustados con ColorMatrix
Figura 10: Brillo y contraste de la imagen ajustados con ColorMatrix

Cómo invertir los colores de un elemento Image que admite composición

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_desc),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)
Colores invertidos en la imagen
Figura 11: Colores invertidos en la imagen

Cómo desenfocar un elemento Image que admite composición

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_desc),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
             radiusX = 10.dp,
             radiusY = 10.dp,
             edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
         )
)
BlurEffect aplicado a la imagen
Figura 12: BlurEffect aplicado a la 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_desc),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
             radiusX = 10.dp,
             radiusY = 10.dp,
             edgeTreatment = BlurredEdgeTreatment.Unbounded
         )
         .clip(RoundedCornerShape(8.dp))
)
BlurEdgeTreatment.Unbounded
Figura 13: BlurEdgeTreatment.Unbounded