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 adapté :

Image source La source du portrait, qui montre un chien. La source du 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 de 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 portrait recadrée pour remplir un cadre carré, ne montrant que la partie centrale de l'image. Image au format paysage recadrée pour remplir un cadre carré, n'affichant 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. Image 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 au format portrait redimensionnée pour remplir la largeur d'un cadre carré, avec le haut et le bas recadrés. Image au format 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 portrait déformée pour remplir complètement un cadre carré, en étirant l'image. Image au format 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 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 au format portrait, initialement plus grande que ses limites carrées, redimensionnée pour s'adapter tout en conservant ses proportions, 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 au format paysage, initialement plus grande que ses limites carrées, redimensionnée pour s'adapter tout en conservant le format, avec un arrière-plan jaune en haut et en bas. Image source plus petite que les limites : Une image au format Paysage, initialement plus petite que ses limites carrées, est 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 portrait, initialement plus grande que ses limites carrées, affichée à sa taille d'origine, avec des parties qui dépassent le haut et le 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 qui dépassent à gauche et à droite du cadre. Image source plus petite que les limites : Une image au format Paysage, initialement plus petite que ses limites carrées, est 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 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 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 en 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 forme de cercle, avec une bordure jaune autour de la forme circulaire.
Figure 4. Image découpée avec une bordure.

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 plus courte.
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 foncées.
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 la 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 dont la luminosité et le contraste ont été augmentés, ce qui le rend plus éclatant.
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 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 fort effet de flou appliqué, le rendant 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.