Assurez-vous que votre UI fonctionne avec les encarts de fenêtre

Une fois que votre activité a pris le contrôle de la gestion de tous les encarts, vous pouvez utiliser les API Compose pour vous assurer que le contenu n'est pas masqué et que les éléments interactifs ne se chevauchent pas avec l'UI du système. Ces API synchronisent également la mise en page de votre application avec les modifications de l'encart.

Par exemple, voici la méthode la plus simple pour appliquer les encarts au contenu de l'ensemble de votre application:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Cet extrait applique les encarts de fenêtre safeDrawing comme marge intérieure autour de l'ensemble du contenu de l'application. Cela garantit que les éléments interactifs ne se chevauchent pas avec l'UI du système, mais cela signifie également qu'aucun élément de l'application ne sera dessiné derrière l'UI du système pour obtenir un effet bord à bord. Pour exploiter pleinement l'ensemble de la fenêtre, vous devez affiner l'emplacement des encarts par écran ou par composant.

Tous ces types d'encarts sont animés automatiquement avec des animations IME rétroportées vers l'API 21. Par extension, toutes vos mises en page utilisant ces encarts sont également animées automatiquement lorsque les valeurs d'encart changent.

Il existe deux principales façons d'utiliser ces types d'encarts pour ajuster vos mises en page de composables: les modificateurs de marge intérieure et les modificateurs de taille d'encart.

Modificateurs de marge intérieure

Modifier.windowInsetsPadding(windowInsets: WindowInsets) applique les encarts de fenêtre donnés en tant que marge intérieure, comme le ferait Modifier.padding. Par exemple, Modifier.windowInsetsPadding(WindowInsets.safeDrawing) applique les marges intérieures de dessin sécurisées en tant que marge intérieure sur les quatre côtés.

Plusieurs méthodes utilitaires intégrées sont également disponibles pour les types d'encarts les plus courants. Modifier.safeDrawingPadding() est une telle méthode, équivalente à Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Il existe des modificateurs analogues pour les autres types d'encarts.

Modificateurs de taille de l'encart

Les modificateurs suivants appliquent une quantité d'encarts de fenêtre en définissant la taille du composant sur la taille des encarts:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Applique le côté de début de windowInsets comme largeur (comme Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Applique le côté final de windowInsets comme largeur (comme Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Applique la partie supérieure de windowInsets comme hauteur (comme Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Applique la partie inférieure de windowInsets comme hauteur (comme Modifier.height)

Ces modificateurs sont particulièrement utiles pour dimensionner un Spacer qui occupe l'espace des encarts:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Consommation d'incrustations

Les modificateurs de marge intérieure (windowInsetsPadding et les outils d'assistance tels que safeDrawingPadding) consomment automatiquement la partie des marges intérieures appliquées en tant que marge intérieure. En approfondissant l'arborescence de composition, les modificateurs de marge intérieure imbriqués et les modificateurs de taille d'encart savent qu'une partie des encarts a déjà été utilisée par les modificateurs de marge intérieure externe, et évitent d'utiliser la même partie des encarts plusieurs fois, ce qui entraînerait trop d'espace supplémentaire.

Les modificateurs de taille d'encart évitent également d'utiliser la même partie d'encart plusieurs fois si les encarts ont déjà été consommés. Toutefois, comme ils modifient directement leur taille, ils ne consomment pas eux-mêmes d'encarts.

Par conséquent, les modificateurs de marge intérieure imbriqués modifient automatiquement la quantité de marge intérieure appliquée à chaque composable.

En examinant le même exemple LazyColumn qu'auparavant, le LazyColumn est redimensionné par le modificateur imePadding. Dans LazyColumn, la taille du dernier élément correspond à la hauteur du bas des barres système:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Lorsque l'IME est fermé, le modificateur imePadding() n'applique aucune marge intérieure, car l'IME n'a pas de hauteur. Étant donné que le modificateur imePadding() n'applique aucune marge intérieure, aucune encoche n'est consommée, et la hauteur de Spacer correspond à la taille de la partie inférieure des barres système.

Lorsque l'IME s'ouvre, les encarts de l'IME s'animent pour correspondre à la taille de l'IME, et le modificateur imePadding() commence à appliquer une marge intérieure inférieure pour redimensionner le LazyColumn lorsque l'IME s'ouvre. Lorsque le modificateur imePadding() commence à appliquer la marge inférieure, il commence également à consommer cette quantité de marges intérieures. Par conséquent, la hauteur de Spacer commence à diminuer, car une partie de l'espacement des barres système a déjà été appliquée par le modificateur imePadding(). Une fois que le modificateur imePadding() applique une marge intérieure inférieure supérieure aux barres système, la hauteur de Spacer est nulle.

Lorsque l'IME se ferme, les modifications se produisent dans l'ordre inverse: Spacer commence à se développer à partir d'une hauteur de zéro une fois que imePadding() s'applique moins que la partie inférieure des barres système, jusqu'à ce que Spacer corresponde à la hauteur de la partie inférieure des barres système une fois que l'IME est complètement animé.

Figure 2. Colonne paresseuse de bord à bord avec TextField.

Ce comportement est obtenu par la communication entre tous les modificateurs windowInsetsPadding et peut être influencé de plusieurs autres manières.

Modifier.consumeWindowInsets(insets: WindowInsets) consomme également des encarts de la même manière que Modifier.windowInsetsPadding, mais n'applique pas les encarts consommés en tant que marge intérieure. Cela est utile en combinaison avec les modificateurs de taille des encarts pour indiquer aux frères et sœurs qu'une certaine quantité d'encarts a déjà été consommée:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) se comporte de manière très similaire à la version avec un argument WindowInsets, mais prend un PaddingValues arbitraire à consommer. Cela permet d'informer les enfants lorsque la marge intérieure ou l'espacement est fourni par un autre mécanisme que les modificateurs de marge intérieure, tels qu'un Modifier.padding ordinaire ou des espaces de hauteur fixe:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

Lorsque les encarts de fenêtre bruts sont nécessaires sans consommation, utilisez directement les valeurs WindowInsets ou WindowInsets.asPaddingValues() pour renvoyer un PaddingValues des encarts non affectés par la consommation. Toutefois, en raison des mises en garde ci-dessous, préférez utiliser les modificateurs de marge intérieure et de taille des encarts de fenêtre dans la mesure du possible.

Insets et phases de Jetpack Compose

Compose utilise les API principales AndroidX sous-jacentes pour mettre à jour et animer les insets, qui utilisent les API de plate-forme sous-jacentes qui gèrent les insets. En raison de ce comportement de la plate-forme, les encarts ont une relation particulière avec les phases de Jetpack Compose.

La valeur des marges intérieures est mise à jour après la phase de composition, mais avant la phase de mise en page. Cela signifie que la lecture de la valeur des encarts dans la composition utilise généralement une valeur des encarts qui est retardée d'un frame. Les modificateurs intégrés décrits sur cette page sont conçus pour retarder l'utilisation des valeurs des marges intérieures jusqu'à la phase de mise en page, ce qui garantit que les valeurs de marge intérieure sont utilisées sur le même frame qu'elles sont mises à jour.