La plate-forme Android est chargée de dessiner l'UI du système, comme 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 l'UI du système pour s'assurer que votre application dessine dans la zone appropriée et que votre UI n'est pas masquée par l'UI du système.
Sous Android 14 (niveau d'API 34) et les versions antérieures, l'UI de votre application ne s'affiche pas sous 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 les barres du système et affiche des découpes une fois qu'elle 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.
L'affichage du contenu derrière l'UI du système s'appelle le bord à bord. Sur cette page, vous découvrirez les différents types d'encarts, comment aller d'un bord à l'autre et comment utiliser les API d'encart pour animer votre UI et vous assurer que le contenu de votre application n'est pas masqué 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 importants ne sont pas masqués par l'interface utilisateur 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'interface utilisateur du système et les informations sur son emplacement sont spécifiées via des encarts.
Chaque partie de l'interface utilisateur du système est associée à 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 de 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. Par conséquent, pour éviter tout chevauchement avec ce type d'interface utilisateur système, l'interface utilisateur de l'application doit être en retrait de cette valeur.
Ces types d'encarts Android intégrés sont disponibles via WindowInsets
:
Les encarts décrivant les barres d'état. Il s'agit des barres d'interface utilisateur système supérieures contenant des icônes de notification et d'autres indicateurs. |
|
Encoches de la barre d'état lorsqu'elles sont visibles. Si les barres d'état sont actuellement masquées (en raison de l'activation du mode plein écran immersif), les encarts de la barre d'état principale sont vides, mais ils ne le sont pas. |
|
Les encarts décrivant les barres de navigation. Il s'agit des barres de l'interface utilisateur système situées à gauche, à droite ou en bas de l'appareil, qui décrivent la barre des tâches ou les icônes de navigation. Ils 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 ses interactions avec la barre des tâches. |
|
La barre de navigation est en retrait lorsqu'elle est visible. 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. |
|
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. |
|
La barre de sous-titres est en retrait lorsqu'elle est visible. Si les barres de sous-titres sont actuellement masquées, les encarts de la barre de sous-titres principale sont vides, mais ces encarts ne sont pas vides. |
|
Union des encarts de la barre système, y compris les barres d'état, les barres de navigation et la barre de légende. |
|
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. |
|
Les encarts décrivant la quantité d'espace occupée par le clavier logiciel en bas. |
|
Les encarts décrivant l'espace occupé par le clavier logiciel avant l'animation du clavier actuelle. |
|
Les encarts décrivant l'espace que le clavier logiciel occupera après l'animation du clavier actuelle. |
|
Type d'encarts décrivant des informations plus détaillées sur l'UI de navigation, indiquant la quantité d'espace où les "appuis" seront gérés par le système et non par l'application. Pour les barres de navigation transparentes avec navigation par gestes, certains éléments de l'application peuvent être enfoncés via l'UI de navigation du système. |
|
Les éléments cliquables sont en retrait lorsqu'ils sont visibles. Si les éléments cliquables sont actuellement masqués (en raison de l'activation du mode plein écran immersif), les encarts principaux des éléments cliquables seront vides, mais ces encarts ne seront pas vides. |
|
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 |
|
Sous-ensemble des gestes système qui seront toujours gérés par le système et qui ne peuvent pas être désactivés via |
|
Les encarts représentant l'espacement nécessaire pour éviter tout chevauchement avec une découpe d'écran (encoche ou trou d'épingle). |
|
Les encarts représentant les zones incurvées d'un affichage en cascade. Un écran en cascade présente 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 insets: pour éviter de dessiner du contenu masqué par l'UI du système (partiellement ou complètement). - Utilisez
WindowInsets.safeGestures
pour protéger le contenu à l'aide de gestes. Cela évite que les gestes système ne se heurtent aux gestes de l'application (comme ceux des bottom sheets, des carrousels ou des jeux). - Utilisez
WindowInsets.safeContent
en combinantWindowInsets.safeDrawing
etWindowInsets.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 peut dessiner des couleurs noires ou unies derrière l'UI du système, ou ne pas s'animer de manière synchrone avec le clavier logiciel.
- Ciblez le SDK 35 ou une version ultérieure pour appliquer l'affichage bord à bord sur Android 15 et les versions ultérieures. Votre application s'affiche derrière l'UI du système. Vous pouvez ajuster l'UI de votre application en gérant les encarts.
- Vous pouvez également appeler
enableEdgeToEdge()
dansActivity.onCreate()
, ce qui permet à votre application d'être de bord à bord sur les versions précédentes d'Android. Définissez
android:windowSoftInputMode="adjustResize"
dans l'entréeAndroidManifest.xml
de votre activité. Ce paramètre permet à votre application de recevoir la taille de l'IME logiciel sous forme d'encarts, que vous pouvez utiliser pour rembourrer et mettre en page le contenu de manière appropriée 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 fenêtre safeDrawing
en tant que 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 s'affichera 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:
Applique le côté de début de windowInsets comme largeur (comme |
|
Applique le côté final de windowInsets comme largeur (comme |
|
Applique la partie supérieure de windowInsets comme hauteur (comme |
|
|
Applique la partie inférieure de windowInsets comme hauteur (comme |
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: le Spacer
commence à se développer à partir d'une hauteur de zéro une fois que le imePadding()
s'applique moins que la partie inférieure des barres système, jusqu'à ce que le Spacer
corresponde à la hauteur de la partie inférieure des barres système une fois que l'IME est complètement animé.
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.
Animations IME du clavier avec WindowInsets
Vous pouvez appliquer Modifier.imeNestedScroll()
à un conteneur à défilement pour ouvrir et fermer automatiquement le IME lorsque vous faites défiler le conteneur jusqu'en bas.
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") } } }
Prise en charge des composants Material 3 dans les encarts
Pour faciliter l'utilisation, de nombreux composables Material 3 intégrés (androidx.compose.material3
) gèrent eux-mêmes les encarts, en fonction de la façon dont les composables sont placés dans votre application conformément aux spécifications Material.
Composables de gestion des encarts
Vous trouverez ci-dessous la liste des composants Material qui gèrent automatiquement les encarts.
Barres d'application
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: applique les côtés supérieur et horizontal des barres système comme marge intérieure, car ils sont utilisés en haut de la fenêtre.BottomAppBar
: applique les côtés inférieur et horizontal des barres système en tant que marge intérieure.
Conteneurs
ModalDrawerSheet
/DismissibleDrawerSheet
/PermanentDrawerSheet
(contenu dans un panneau de navigation modal): applique des marges intérieures verticales et de début au contenu.ModalBottomSheet
: applique les marges intérieures inférieures.NavigationBar
: applique les marges intérieures inférieures et horizontales.NavigationRail
: applique les marges intérieures verticales et de début.
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 insets 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 la gestion 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 View
Vous devrez peut-être remplacer les marges intérieures par défaut lorsque votre écran contient à la fois des vues et du 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 la plus externe est une mise en page de vue Android, vous devez utiliser les marges intérieures dans le système de vue 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 insets au niveau de consommation WindowInsetsCompat
. Pour modifier ce comportement par défaut, définissez ComposeView.consumeWindowInsets
sur false
.
Protection de la barre système
Une fois que votre application cible le SDK 35 ou une version ultérieure, l'affichage bord à bord est appliqué. La barre d'état du système et les barres de navigation par gestes sont transparentes, mais la barre de navigation à trois boutons est translucide.
Pour supprimer la protection de l'arrière-plan de la navigation à trois boutons translucide par défaut, définissez Window.setNavigationBarContrastEnforced
sur false
.
Ressources
- Barres système Android, conseils de conception des barres système
- Now in Android : application Android entièrement fonctionnelle, développée entièrement avec Kotlin et Jetpack Compose.
- Gérer les mesures d'application bord à bord dans Android 15 : atelier de programmation expliquant la mesure d'application bord à bord d'Android 15
- Conseils de gestion des marges intérieures pour l'application bord à bord d'Android 15
- Prévisualiser et tester l'UI bord à bord de votre application
- 3 éléments pour améliorer l'expérience dans votre application Android : Edge to Edge, la prévisualisation du Retour et Glance : vidéo YouTube sur l'application d'Edge to Edge sur Android 15
- Bord à bord et encarts | Conseils pour Compose : vidéo YouTube montrant comment gérer les encarts pour dessiner de bord à bord
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Composants et mises en page Material
- Migrer
CoordinatorLayout
vers Compose - Autres points à prendre en compte