Affichez le contenu bord à bord dans votre application et gérez les encarts dans Compose

La plate-forme Android est chargée de dessiner l'UI du système, par exemple la barre d'état et la barre de navigation. Cette interface utilisateur du système s'affiche quelle que soit l'application utilisée par l'utilisateur.

WindowInsets fournit des informations sur le système. UI pour s'assurer que votre application s'affiche dans la bonne zone et qu'elle n'est pas masquée par l'UI du système.

Affichage de bord à bord pour dessiner derrière les barres système
Figure 1. Affichage bord à bord pour dessiner derrière les barres système.

Sur Android 14 (niveau d'API 34) ou version antérieure, l'interface utilisateur de votre application ne s'affiche pas en dessous. les barres système et les encoches par défaut.

Sur Android 15 (niveau d'API 35) ou version ultérieure, votre application s'affiche sous le système. barres et encoches une fois que votre application cible le SDK 35. Cela offre une expérience utilisateur plus fluide et permet à votre application de tirer pleinement parti de l'espace de fenêtre dont elle dispose.

Le contenu affiché derrière l'interface utilisateur du système est appelé bord à bord. Sur ce page, vous découvrirez les différents types d'encarts, comment passer de bord à bord, et comment utiliser les API d'encart pour animer votre interface utilisateur et vous assurer que le contenu de votre application n'est pas masquée par les éléments d'UI du système.

Principes de base des encarts

Lorsqu'une application s'affiche de bord à bord, vous devez vous assurer que le contenu et les interactions ne sont pas masquées par l'UI du système. Par exemple, si un bouton est placé derrière la barre de navigation, l'utilisateur ne pourra peut-être pas cliquer dessus.

la taille de l'UI du système et des informations sur son emplacement sont spécifiés ; via des encarts.

Chaque partie de l'UI du système possède un type d'encart correspondant qui décrit sa taille et son emplacement. Par exemple, les encarts de la barre d'état indiquent la taille et la position de la barre d'état, tandis que les encarts de la barre de navigation indiquent la taille et la position de la barre de navigation. Chaque type d'encart se compose de quatre dimensions en pixels: haut, gauche, droite et bas. Ces dimensions spécifient la distance à laquelle l'interface utilisateur du système s'étend à partir des côtés correspondants de la fenêtre de l'application. Pour éviter chevauchant ce type d'UI du système, l'UI de l'application doit donc être insérée montant.

Ces types d'encarts Android intégrés sont disponibles via WindowInsets :

WindowInsets.statusBars

Les encarts décrivant les barres d'état. Les barres supérieures de l'interface utilisateur du système contiennent les icônes de notification et d'autres indicateurs.

WindowInsets.statusBarsIgnoringVisibility

Encastrement de la barre d'état lorsqu'elle est visible. Si les barres d'état sont actuellement masquées (en raison du passage en mode immersif en plein écran), les encarts de la barre d'état principale seront vides, mais ils ne le seront pas.

WindowInsets.navigationBars

Encarts décrivant les barres de navigation. Il s'agit des barres d'UI du système à gauche, à droite ou en bas de l'appareil. Elles décrivent la barre des tâches ou les icônes de navigation. Ceux-ci peuvent changer au moment de l'exécution en fonction de la méthode de navigation préférée de l'utilisateur et de l'interaction avec la barre des tâches.

WindowInsets.navigationBarsIgnoringVisibility

La barre de navigation s'insère dans des encarts pour indiquer quand ils sont visibles. Si les barres de navigation sont actuellement masquées (en raison de l'activation du mode plein écran immersif), les encarts de la barre de navigation principale sont vides, mais ils ne le sont pas.

WindowInsets.captionBar

Insère décrivant la décoration de la fenêtre de l'UI du système dans une fenêtre de forme libre, comme la barre de titre supérieure.

WindowInsets.captionBarIgnoringVisibility

La barre de sous-titres est encadrée pour indiquer quand ils sont visibles. Si les barres de sous-titres sont actuellement masquées, les encarts de la barre de sous-titres principale seront vides, mais ils le seront.

WindowInsets.systemBars

Union des encarts de la barre système, y compris les barres d'état, les barres de navigation et la barre de légende.

WindowInsets.systemBarsIgnoringVisibility

Encastrement de la barre système lorsqu'elle est visible. Si les barres système sont actuellement masquées (en raison de l'activation du mode plein écran immersif), les encarts de la barre système principale sont vides, mais ils ne le sont pas.

WindowInsets.ime

Encarts décrivant l'espace en bas que le clavier virtuel occupe.

WindowInsets.imeAnimationSource

Encarts décrivant l'espace occupé par le clavier virtuel avant l'animation actuelle du clavier.

WindowInsets.imeAnimationTarget

Les encarts décrivant l'espace que le clavier virtuel occupera après l'animation du clavier actuelle.

WindowInsets.tappableElement

Type d'encarts décrivant des informations plus détaillées sur l'interface utilisateur de navigation, donnant la quantité d'espace où des « appuis » seront gérés par le système, et non par l'application. Pour les barres de navigation transparentes avec navigation par gestes, il est possible d'appuyer sur certains éléments de l'application via l'UI de navigation du système.

WindowInsets.tappableElementIgnoringVisibility

Les éléments cliquables sont en retrait lorsqu'ils sont visibles. Si les éléments tactiles sont actuellement masqués (en raison du passage en mode immersif en plein écran), les encarts principaux des éléments tactiles seront vides, mais ils ne seront pas vides.

WindowInsets.systemGestures

Les encarts représentant le nombre d'encoches où le système interceptera les gestes de navigation. Les applications peuvent spécifier manuellement la gestion d'un nombre limité de ces gestes via Modifier.systemGestureExclusion.

WindowInsets.mandatorySystemGestures

Sous-ensemble de gestes système qui seront toujours gérés par le système et qui ne peuvent pas être désactivés via Modifier.systemGestureExclusion.

WindowInsets.displayCutout

Les encarts représentant l'espacement nécessaire pour éviter de chevaucher une encoche (encoche ou trou d'épingle).

WindowInsets.waterfall

Les encarts représentant les zones incurvées d'un affichage en cascade. Un écran en cascade comporte des zones incurvées sur les bords, là où l'écran commence à se replier sur les côtés de l'appareil.

Ces types sont résumés par trois types d'incrustations "sûres" qui garantissent que le contenu n'est pas masqué :

Ces types d'encarts "sûrs" protègent le contenu de différentes manières, en fonction des encarts de la plate-forme sous-jacente :

  • Utilisez WindowInsets.safeDrawing pour protéger le contenu qui ne doit pas être dessiné sous l'interface utilisateur du système. Il s'agit de l'utilisation la plus courante des encarts: pour éviter en dessinant du contenu obscurci par l'interface utilisateur du système (que ce soit partiellement ou complètement).
  • Utilisez WindowInsets.safeGestures pour protéger des contenus à l'aide de gestes. Ce évite les conflits entre les gestes système et les gestes de l'application (tels que ceux les feuilles, les carrousels ou les jeux).
  • Utilisez WindowInsets.safeContent en combinant WindowInsets.safeDrawing et WindowInsets.safeGestures pour vous assurer que le contenu ne se chevauche pas visuellement ni par gestes.

Configuration des encarts

Pour permettre à votre application de contrôler entièrement l'emplacement où elle dessine le contenu, suivez ces étapes de configuration. Sans ces étapes, votre application risque de dessiner des couleurs noires ou unies derrière le ou ne pas s'animer de manière synchrone avec le clavier virtuel.

  1. Ciblez le SDK 35 ou version ultérieure pour appliquer de bord à bord sous Android 15 ou version ultérieure. Votre application derrière l'UI du système. Vous pouvez ajuster l'interface utilisateur de votre application en gérant les encarts.
  2. Vous pouvez également appeler enableEdgeToEdge() dans Activity.onCreate(), ce qui permet à votre application d'être de bord à bord sur les versions précédentes d'Android.
  3. Définissez android:windowSoftInputMode="adjustResize" dans l'entrée AndroidManifest.xml de votre activité. Ce paramètre permet à votre application de recevoir la taille de l'IME du logiciel sous forme d'encarts, que vous pouvez utiliser pour remplir et mettre en page le contenu correctement lorsque l'IME apparaît et disparaît dans votre application.

    <!-- in your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

API Compose

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 d'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 la fenêtre safeDrawing en tant que marge intérieure autour de l'intégralité du contenu de l'application. Bien que cela garantit que les éléments interactifs n’ont pas avec l'UI du système, cela signifie aussi qu'aucune application pour obtenir un effet bord à bord. Pour exploiter pleinement l'ensemble vous devez ajuster l'emplacement des encarts dans un écran par écran. composant 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 automatiquement animée lorsque les valeurs d'encart changent.

Il existe deux façons principales d'utiliser ces types d'encarts pour ajuster votre composable mises en page: modificateurs de marge intérieure et 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.

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

Modificateurs de taille de l'encart

Les modificateurs suivants appliquent un certain nombre d'encarts de fenêtre en définissant la taille le composant à la taille des encarts:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

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

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Applique la fin des encarts windowInsets en tant que largeur (comme Modifier.width).

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Applique la partie supérieure des encarts windowInsets en tant que 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 une Spacer qui occupe d'encarts:

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

Consommation d'encarts

Les modificateurs de marge intérieure (windowInsetsPadding et les assistants tels que safeDrawingPadding) consomment automatiquement la partie des encarts appliqué comme remplissage. Pour approfondir l'arborescence de composition, l'encart imbriqué les modificateurs de marge intérieure et les modificateurs de taille d'encart savent qu'une partie les encarts sont déjà utilisés par les modificateurs de marge intérieure d'encart externes, et évitez utiliser la même partie des encarts plusieurs fois, ce qui entraînerait trop beaucoup plus 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. Cependant, puisqu'ils sont en train de changer les encarts ne sont pas directement utilisés.

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, le dernier élément est adapté à 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, et aucun encart n'est utilisé, et la hauteur de Spacer correspond à la taille le côté inférieur des barres système.

Lorsque l'IME s'ouvre, les encarts s'animent pour correspondre à la taille de l'IME et Le modificateur imePadding() commence à appliquer une marge intérieure inférieure pour redimensionner la LazyColumn à l'ouverture de l'IME. Lorsque le modificateur imePadding() commence à s'appliquer une marge intérieure inférieure, il commence également à consommer cette quantité d'encarts. Par conséquent, le la hauteur de Spacer commence à diminuer pour l'espacement du système. barres ont déjà été appliquées par le modificateur imePadding(). Une fois que Le modificateur imePadding() applique une marge intérieure inférieure plus importante que les barres système, la hauteur de Spacer est égale à zéro.

Lorsque l'IME se ferme, les modifications se produisent à l'envers: Spacer commence à à partir d'une hauteur de zéro une fois que imePadding() applique moins que en bas des barres système, jusqu'à ce que Spacer corresponde à la hauteur le côté inférieur des barres système une fois que l'IME est complètement animé.

Figure 2. Colonne différée de bord à bord avec TextField.

Ce comportement est obtenu grâce à la communication entre tous les modificateurs windowInsetsPadding, et peuvent être influencés dans d'autres de différentes 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.

Phases d'encarts et 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. Grâce à cette plate-forme, d'entraînement, les encarts ont une relation particulière avec les phases de Jetpack Nouveau message.

La valeur des encarts est mise à jour après la phase de composition, mais avant la phase 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.

Animations IME du clavier avec WindowInsets

Vous pouvez appliquer Modifier.imeNestedScroll() à un conteneur à défilement pour ouvrir et ferme automatiquement l'IME lors du défilement jusqu'au bas du conteneur.

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

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

Animation montrant un élément d&#39;interface utilisateur glissant vers le haut ou vers le bas pour laisser place à un clavier
Figure 3. Animations IME

Compatibilité des encarts avec les composants Material 3

Pour plus de simplicité, de nombreux composables Material 3 intégrés (androidx.compose.material3) gèrent les encarts eux-mêmes, en fonction de la manière dont les composables sont placés dans votre application. conformément aux spécifications de Material.

Composables de gestion des encarts

Vous trouverez ci-dessous la liste des ressources des composants gèrent automatiquement les encarts.

Barres d'application

Conteneurs

Scaffold

Par défaut, Scaffold fournit des encarts en tant que paramètre paddingValues que vous pouvez utiliser. Scaffold n'applique pas les encarts au contenu. Cette responsabilité vous incombe. Par exemple, pour utiliser ces encarts avec un LazyColumn dans un Scaffold:

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

Ignorer les marges intérieures par défaut

Vous pouvez modifier le paramètre windowInsets transmis au composable pour configurer son comportement. Ce paramètre peut être un autre type d'encart de fenêtre à appliquer à la place, ou désactivé en transmettant une instance vide : WindowInsets(0, 0, 0, 0).

Par exemple, pour désactiver le traitement des encarts sur LargeTopAppBar, définissez le paramètre windowInsets sur une instance vide:

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

Interopérabilité avec les encarts du système de vues

Vous devrez peut-être remplacer les encarts par défaut si votre écran dispose à la fois de vues et Code Compose dans la même hiérarchie. Dans ce cas, vous devez indiquer explicitement lequel doit consommer les encarts et lequel doit les ignorer.

Par exemple, si votre mise en page externe est une mise en page Android View, vous devez utiliser les encarts dans le système de vues et les ignorer pour Compose. Si votre mise en page la plus externe est un composable, vous devez consommer les marges intérieures dans Compose et ajouter un espace aux composables AndroidView en conséquence.

Par défaut, chaque ComposeView consomme tous les encarts au Niveau de consommation de WindowInsetsCompat. Pour modifier ce comportement par défaut, définissez ComposeView.consumeWindowInsets sur false.

Ressources