Personnaliser une image

Vous pouvez personnaliser les images à l'aide des propriétés sur un composable Image (contentScale, colorFilter). Vous pouvez également appliquer les modificateurs existants pour appliquer différents effets à votre Image. Vous pouvez utiliser les modificateurs sur n'importe quel composable, pas seulement sur le composable Image, tandis que contentScale et colorFilter sont des paramètres explicites sur le composable Image.

Échelle de contenu

Spécifiez une option contentScale pour recadrer ou modifier l'ajustement d'une image dans ses limites. Par défaut, si vous ne spécifiez pas l'option contentScale, ContentScale.Fit est utilisé.

Dans l'exemple suivant, la taille du composable Image est limitée à 150 dp avec une bordure, et l'arrière-plan est défini sur jaune sur le composable Image, pour présenter les différentes options ContentScale du tableau ci-dessous.

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 vous définissez d'autres options ContentScale, les résultats seront différents. Le tableau suivant vous aide à choisir le mode ContentScale approprié :

Image source La source du portrait, qui montre un chien. La source en mode paysage, qui montre un autre chien.
ContentScale Résultat – Image au format Portrait : Résultat – Image au format Paysage :
ContentScale.Fit : ajuste l'image de manière uniforme, en conservant le format (par défaut). Si le contenu est plus petit, l'image est ajustée pour respecter les limites. Portrait d'un chien mis à l'échelle de manière uniforme. Paysage avec un chien mis à l'échelle de manière uniforme.
ContentScale.Crop : recadre et centre l'image dans l'espace disponible. Image en mode Portrait recadrée pour remplir un cadre carré, ne montrant que la partie centrale de l'image. Image en mode paysage recadrée pour remplir un cadre carré, ne montrant que la partie centrale de l'image.
ContentScale.FillHeight : ajuste la source en conservant le format afin que les limites correspondent à la hauteur de la destination. Une image en mode portrait redimensionnée pour remplir la hauteur d'un cadre carré, montrant l'image complète avec un arrière-plan jaune visible à gauche et à droite. Image en mode paysage redimensionnée pour remplir la hauteur d'un cadre carré, avec les côtés recadrés.
ContentScale.FillWidth : ajuste la source en conservant le format afin que les limites correspondent à la largeur de la destination. Image en mode portrait redimensionnée pour remplir la largeur d'un cadre carré, avec le haut et le bas recadrés. Une image en mode paysage redimensionnée pour remplir la largeur d'un cadre carré, montrant l'image complète avec un arrière-plan jaune visible en haut et en bas.
ContentScale.FillBounds : ajuste le contenu verticalement et horizontalement de manière non uniforme afin de remplir les limites de la destination. Remarque : cela entraîne une distorsion des images si vous les placez dans des conteneurs dont le format ne correspond pas exactement à celui de l'image. Image en mode portrait déformée pour remplir complètement un cadre carré, ce qui étire l'image. Image en mode paysage déformée pour remplir complètement un cadre carré, ce qui étire l'image.
ContentScale.Inside : ajuste la source pour conserver le format dans les limites de la destination. Si la source est de taille inférieure ou égale à celle de la destination dans les deux dimensions, le comportement est le même qu'avec l'option None. Le contenu sera toujours contenu dans les limites. Si le contenu est plus petit que les limites, aucun ajustement ne sera réalisé. Image source plus grande que les limites : Image en mode portrait, initialement plus grande que ses limites carrées, réduite pour s'adapter tout en conservant son format, avec un arrière-plan jaune sur les côtés. Image source plus petite que les limites : Image au format portrait, initialement plus petite que ses limites carrées, affichée à sa taille d'origine dans le cadre, avec un arrière-plan jaune autour. Image source plus grande que les limites : Image en mode paysage, initialement plus grande que ses limites carrées, réduite pour s'adapter tout en conservant son format, avec un arrière-plan jaune en haut et en bas. Image source plus petite que les limites : Image au format paysage, initialement plus petite que ses limites carrées, affichée à sa taille d'origine dans le cadre, avec un arrière-plan jaune autour.
ContentScale.None : n'ajuste pas la source. Si le contenu est plus petit que les limites de la destination, il ne sera pas ajusté pour s'adapter à la zone. Image source plus grande que les limites : Image en mode portrait, initialement plus grande que ses limites carrées, affichée à sa taille d'origine, avec des parties dépassant en haut et en bas du cadre. Image source plus petite que les limites : Image au format portrait, initialement plus petite que ses limites carrées, affichée à sa taille d'origine dans le cadre, avec un arrière-plan jaune autour. Image source plus grande que les limites : Image au format paysage, initialement plus grande que ses limites carrées, affichée à sa taille d'origine, avec des parties dépassant à gauche et à droite du cadre. Image source plus petite que les limites : Image au format paysage, initialement plus petite que ses limites carrées, affichée à sa taille d'origine dans le cadre, avec un arrière-plan jaune autour.

Découper un composable Image selon une forme

Pour ajuster une image à une forme, utilisez le modificateur clip intégré. Pour recadrer une image dans un cercle, utilisez 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)
)

Image d'un chien découpée en un cercle parfait.
Figure 1. Découpe d'une image avec CircleShape.

Pour une forme d'angle arrondie, utilisez Modifier.clip(RoundedCornerShape(16.dp)) avec la taille des angles que vous souhaitez arrondir :

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

Image carrée d'un chien découpée avec des angles arrondis.
Figure 2. Découpe d'une image avec RoundedCornerShape.

Vous pouvez également créer votre propre forme de découpage en étendant Shape et en fournissant un Path pour que l'image soit découpée selon la forme spécifiée :

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

Image carrée d'un chien découpée selon une forme ovale personnalisée.
Figure 3. Découpe d'une image selon un contour personnalisé

Ajouter une bordure à un composable Image

Une opération courante consiste à combiner Modifier.border() et Modifier.clip() pour créer une bordure autour d'une image :

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

Image carrée d'un chien, découpée en cercle, avec une bordure jaune autour de la forme circulaire.
Figure 4. Image découpée et encadrée

Pour créer une bordure en dégradé, vous pouvez utiliser l'API Brush pour dessiner une bordure en dégradé arc-en-ciel autour de l'image :

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

Image circulaire d'un chien avec une bordure en dégradé arc-en-ciel autour de la forme circulaire.
Figure 5 : Bordure circulaire en dégradé arc-en-ciel.

Définir un format personnalisé

Pour convertir une image dans un format personnalisé, utilisez Modifier.aspectRatio(16f/9f) pour fournir un format personnalisé pour une image (ou tout autre composable).

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

Image carrée d'un chien, transformée au format 16:9, ce qui la rend plus large et moins haute.
Figure 6 : Utiliser Modifier.aspectRatio(16f/9f) sur un Image.

Filtre de couleur : transformer la couleur des pixels d'une image

Le composable Image possède un paramètre colorFilter, qui peut modifier le rendu de chaque pixel de votre image.

Teinter des images

L'utilisation de ColorFilter.tint(color, blendMode) applique à votre composable Image un mode de fusion avec la couleur donnée. ColorFilter.tint(color, blendMode) utilise BlendMode.SrcIn pour teinter le contenu, ce qui signifie que la couleur fournie est affichée là où l'image est affichée à l'écran. Cette fonction est utile pour les icônes et les vecteurs qui doivent être traités selon un thème différent.

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

Image d'un bus avec une teinte jaune appliquée.
Figure 7 : ColorFilter.tint appliqué avec BlendMode.SrcIn.

Les autres BlendMode produisent des effets différents. Par exemple, si vous définissez BlendMode.Darken avec Color.Green sur une image, vous obtenez le résultat suivant :

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

Chien avec une teinte verte appliquée à l'aide de BlendMode.Darken, ce qui donne des nuances de vert plus sombres.
Figure 8 : Color.Green tint avec BlendMode.Darken.

Pour en savoir plus sur les différents modes de fusion disponibles, consultez la documentation de référence pour BlendMode.

Appliquer un filtre Image avec une matrice de couleur

Transformez votre image avec l'option ColorFilter de la matrice de couleur. Par exemple, pour appliquer un filtre noir et blanc à vos images, vous pouvez utiliser ColorMatrix et régler la saturation sur 0f.

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

Un chien avec un filtre noir et blanc appliqué, supprimant toute saturation des couleurs.
Figure 9 : Matrice de couleur avec saturation 0 (image en noir et blanc).

Régler le contraste ou la luminosité d'un composable Image

Pour modifier le contraste et la luminosité d'une image, vous pouvez utiliser la ColorMatrix pour modifier les valeurs :

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 chien avec une luminosité et un contraste accrus, ce qui le rend plus net.
Figure 10 : Réglage de la luminosité et du contraste de l'image à l'aide de ColorMatrix.

Inverser les couleurs d'un composable Image

Pour inverser les couleurs d'une image, définissez la ColorMatrix de manière à inverser les couleurs :

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 chien dont les couleurs sont inversées, ce qui donne un effet de négatif.
Figure 11 : Couleurs inversées sur l'image.

Flouter un composable Image

Pour flouter une image, utilisez Modifier.blur(), en fournissant les valeurs radiusX et radiusY pour spécifier le rayon de floutage horizontal et vertical, respectivement.

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 chien avec un effet de flou prononcé, ce qui le rend indistinct et flou.
Figure 12 : BlurEffect appliqué à une image.

Lors du floutage d'Images, il est recommandé d'utiliser BlurredEdgeTreatment(Shape) au lieu de BlurredEdgeTreatment.Unbounded. En effet, ce dernier est utilisé pour flouter les rendus arbitraires censés s'afficher en dehors des limites du contenu d'origine. Pour ce qui est des images, il est probable qu'elles s'affichent en dehors des limites du contenu. En revanche, flouter un rectangle arrondi peut nécessiter cette distinction.

Par exemple, si vous définissez BlurredEdgeTreatment sur Unbounded pour l'image précédente, les bords de l'image seront flous au lieu d'être nets :

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

Image floue d'un chien, où le flou s'étend au-delà des limites de l'image d'origine, ce qui rend les bords flous.
Figure 13 : BlurEdgeTreatment.Unbounded.