À propos de WindowInsetsRulers

WindowInsets est l'API standard de Jetpack Compose pour gérer les zones de l'écran qui sont partiellement ou totalement masquées par l'UI système. Ces zones incluent la barre d'état, la barre de navigation et le clavier à l'écran. Vous pouvez également transmettre des WindowInsetsRulers prédéfinis, comme SafeDrawing à Modifier.fitInside ou Modifier.fitOutside, pour aligner votre contenu sur les barres système et l'encoche, ou créer des WindowInsetsRulers personnalisés.

Avantages de WindowInsetsRulers

  • Évite la complexité de la consommation : il fonctionne pendant la phase de placement de la mise en page. Cela signifie qu'il contourne complètement la chaîne de consommation d'encarts et peut toujours fournir les positions absolues correctes des barres système et des encoches d'écran, quels que soient les mises en page parentes. L'utilisation des méthodes Modifier.fitInside ou Modifier.fitOutside est utile pour résoudre les problèmes lorsque les Composables ancêtres consomment incorrectement les encarts.
  • Évitez facilement les barres système : cela aide le contenu de votre application à éviter les barres système et l'encoche, et peut être plus simple que d'utiliser directement WindowInsets.
  • Hautement personnalisable : les développeurs peuvent aligner le contenu sur des règles personnalisées et contrôler précisément leurs mises en page grâce à des mises en page personnalisées.

Inconvénients de WindowInsetsRulers

  • Ne peut pas être utilisé pour la mesure : comme il fonctionne pendant la phase d'emplacement, les informations de position qu'il fournit ne sont pas disponibles lors de la phase de mesure précédente.
  • Instabilité potentielle de la mise en page : cela peut entraîner des plantages si la taille d'une mise en page parente dépend de la taille de ses enfants. Étant donné qu'un enfant utilisant WindowInsetsRulers peut modifier sa position ou sa taille lors du placement, cela peut créer un cycle de mise en page instable.

Créer des WindowInsetsRulers personnalisés

Vous pouvez aligner le contenu sur des règles personnalisées. Par exemple, imaginons un cas d'utilisation où un composable parent gère mal les encarts, ce qui entraîne des problèmes de marge intérieure dans un enfant en aval. Bien que ce problème puisse être résolu d'autres manières, y compris en utilisant Modifier.fitInside, vous pouvez également créer une règle personnalisée pour aligner précisément le composable enfant sans avoir à résoudre le problème dans le composable parent en amont, comme illustré dans l'exemple et la vidéo suivants :

@Composable
fun WindowInsetsRulersDemo(modifier: Modifier) {
    Box(
        contentAlignment = BottomCenter,
        modifier = modifier
            .fillMaxSize()
            // The mistake that causes issues downstream, as .padding doesn't consume insets.
            // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars),
            // assume it's difficult to identify this issue to see how WindowInsetsRulers can help.
            .padding(WindowInsets.navigationBars.asPaddingValues())
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier
                // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child
                // Composable without having to fix the parent upstream.
                .alignToSafeDrawing()

            // .imePadding()
            // .fillMaxWidth()
        )
    }
}

fun Modifier.alignToSafeDrawing(): Modifier {
    return layout { measurable, constraints ->
        if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
            val placeable = measurable.measure(constraints)
            val width = placeable.width
            val height = placeable.height
            layout(width, height) {
                val bottom = WindowInsetsRulers.SafeDrawing.current.bottom
                    .current(0f).roundToInt() - height
                val right = WindowInsetsRulers.SafeDrawing.current.right
                    .current(0f).roundToInt()
                val left = WindowInsetsRulers.SafeDrawing.current.left
                    .current(0f).roundToInt()
                measurable.measure(Constraints.fixed(right - left, height))
                    .place(left, bottom)
            }
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

La vidéo suivante montre un exemple de consommation problématique des encarts IME causée par un parent en amont dans l'image de gauche, et l'utilisation de règles personnalisées pour résoudre le problème à droite. Une marge intérieure supplémentaire s'affiche sous le composable TextField, car la marge intérieure de la barre de navigation n'a pas été utilisée par le parent. L'enfant est placé au bon endroit dans l'image de droite à l'aide d'une règle personnalisée, comme indiqué dans l'exemple de code précédent.

Vérifier que les parents sont contraints

Pour utiliser WindowInsetsRulers de manière sécurisée, assurez-vous que le parent fournit des contraintes valides. Les parents doivent avoir une taille définie et ne peuvent pas dépendre de la taille d'un enfant qui utilise WindowInsetsRulers. Utilisez fillMaxSize ou d'autres modificateurs de taille sur les composables parents.

De même, placer un composable qui utilise WindowInsetsRulers dans un conteneur de défilement tel que verticalScroll peut entraîner un comportement inattendu, car le conteneur de défilement fournit des contraintes de hauteur illimitées, qui sont incompatibles avec la logique de la règle.