La compatibilité avec différentes tailles d'écran permet d'accéder à votre appli sur une large gamme d'appareils et pour un plus grand nombre d'utilisateurs.
Pour accepter un maximum de tailles d'écran (qu'il s'agisse de différents écrans d'appareils ou de différentes fenêtres d'application en mode multifenêtre), concevez vos mises en page d'application de sorte qu'elles soient responsives et adaptatives. Les mises en page responsives et adaptatives offrent une expérience utilisateur optimisée, quelle que soit la taille de l'écran. Elles permettent ainsi à votre application de s'adapter aux téléphones, aux tablettes, aux appareils pliables et aux appareils ChromeOS, ainsi qu'aux orientations portrait et paysage, et aux configurations d'écran redimensionnables comme le mode écran partagé et le fenêtrage sur ordinateur.
Les mises en page responsives/adaptatives changent en fonction de l'espace d'affichage disponible. Les modifications vont de petits ajustements de mise en page qui remplissent l'espace (conception responsive) au remplacement complet d'une mise en page par une autre afin que votre application puisse s'adapter au mieux aux différentes tailles d'écran (conception adaptative).
En tant que kit d'interface utilisateur déclaratif, Jetpack Compose est idéal pour concevoir et implémenter des mises en page qui changent de manière dynamique pour afficher le contenu différemment sur différentes tailles d'écran.
Rendre explicites les changements de mise en page importants pour les composables au niveau du contenu
Les composables au niveau de l'application et du contenu occupent tout l'espace d'affichage disponible pour votre application. Pour ces types de composables, il peut être judicieux de modifier la mise en page globale de votre application sur les grands écrans.
Évitez d'utiliser des valeurs physiques et matérielles pour prendre des décisions concernant la mise en page. Il peut être tentant de prendre des décisions sur la base d'une valeur tangible fixe (l'appareil est-il une tablette ? L'écran physique présente-t-il un certain format ?), mais les réponses à ces questions ne permettent pas forcément de déterminer l'espace disponible pour votre UI.
Sur les tablettes, une application peut s'exécuter en mode multifenêtre, ce qui signifie qu'elle partage l'écran avec une autre application. En mode fenêtré ou sur ChromeOS, une application peut se trouver dans une fenêtre redimensionnable. Il peut même y avoir plusieurs écrans physiques, par exemple avec les pliables. Dans tous ces cas de figure, la taille physique de l'écran n'a pas d'importance pour déterminer comment afficher le contenu.
Vous devez plutôt prendre des décisions basées sur la partie de l'écran qui est réellement allouée à votre application, décrite par les métriques de fenêtre actuelles fournies par la bibliothèque WindowManager de Jetpack. Pour obtenir un exemple d'utilisation de WindowManager dans une application Compose, consultez l'exemple JetNews.
En faisant en sorte que vos mises en page s'adaptent à l'espace disponible sur l'écran, vous réduisez également le nombre de tâches spéciales à prendre en charge pour les plates-formes comme ChromeOS, ainsi que les facteurs de forme tels que les tablettes et les pliables.
Une fois que vous avez déterminé les dimensions de l'espace disponible pour votre application, convertissez la taille brute en classe de taille de fenêtre, comme décrit dans Utiliser les classes de taille de fenêtre. Les classes de taille de fenêtre sont des points d'arrêt conçus pour trouver un juste milieu entre la simplicité de la logique d'application et la flexibilité dans le but d'optimiser votre application pour la plupart des tailles d'écran.
Les classes de taille de fenêtre font référence à la fenêtre globale de votre application. Par conséquent, utilisez-les pour les décisions qui affectent la mise en page globale de votre application. Vous pouvez transmettre les classes de taille de fenêtre en tant qu'état ou appliquer une logique supplémentaire pour créer un état dérivé à transmettre aux composables imbriqués.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
Une approche à plusieurs niveaux confine la logique de taille d'écran à un seul emplacement, au lieu de la disperser dans votre application à de nombreux endroits qui doivent être synchronisés. Un emplacement unique génère un état, qui peut être explicitement transmis à d'autres composables, comme pour tout autre état de l'application. La transmission explicite de l'état simplifie les composables individuels, car ils utilisent la classe de taille de fenêtre ou la configuration spécifiée, ainsi que d'autres données.
Les composables imbriqués flexibles sont réutilisables
Les composables qui peuvent être placés à divers endroits sont plus facilement réutilisables. Si un composable doit être placé à un emplacement spécifique avec une taille spécifique, il est peu probable qu'il soit réutilisable dans d'autres contextes. Cela signifie également que les composables individuels et réutilisables doivent éviter implicitement de dépendre des informations de taille d'affichage globales.
Imaginez un composable imbriqué qui implémente une mise en page liste/détail, qui peut afficher un seul volet ou deux volets côte à côte :
La décision concernant la liste et la vue détaillée doit faire partie de la mise en page globale de l'application. C'est pourquoi elle est transmise à partir d'un composable au niveau du contenu :
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
Et si vous préférez un composable qui modifie sa mise en page indépendamment en fonction de l'espace d'affichage disponible, par exemple une fiche qui présente plus de détails si l'espace le permet ? Une logique basée sur les tailles d'écran disponibles doit être appliquée, mais quelles tailles exactement ?
Évitez de vous baser sur la taille de l'écran de l'appareil. Cette information n'est pas précise pour différents types d'écrans. Elle ne l'est pas non plus si l'application n'est pas en plein écran.
Comme le composable n'est pas un composable au niveau du contenu, n'utilisez pas directement les métriques de la fenêtre actuelle.
Si le composant est placé avec une marge intérieure (par exemple, avec des encarts) ou si l'application inclut des composants tels que des rails de navigation ou des barres d'application, l'espace d'affichage disponible pour le composable peut différer considérablement de l'espace global disponible pour l'application.
Utilisez la largeur donnée au composable pour qu'il s'affiche. Deux options s'offrent à nous pour obtenir cette largeur :
Si vous souhaitez modifier l'emplacement ou le mode d'affichage du contenu, utilisez une collection de modificateurs ou une mise en page personnalisée pour que la mise en page soit responsive. Vous pouvez par exemple demander à un enfant de remplir tout l'espace disponible ou disposer plusieurs éléments enfants avec plusieurs colonnes s'il y a assez d'espace.
Si vous souhaitez modifier ce qui s'affiche, utilisez
BoxWithConstraintscomme alternative plus puissante.BoxWithConstraintsfournit des contraintes de mesure que vous pouvez utiliser pour appeler différents composables en fonction de l'espace d'affichage disponible. Toutefois, cela entraîne des frais, carBoxWithConstraintsretarde la composition jusqu'à la phase de mise en page, lorsque ces contraintes sont connues. Cela implique donc davantage de travail pour la mise en page.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
Rendre toutes les données disponibles pour différentes tailles d'écran
Lorsque vous implémentez un composable qui tire parti de l'espace d'affichage supplémentaire, vous pouvez être tenté d'être efficace et de charger les données en tant qu'effet secondaire de la taille d'affichage actuelle.
Cependant, cela va à l'encontre du principe du flux de données unidirectionnel, qui permet de hisser les données et de les fournir aux composables pour qu'ils s'affichent de manière appropriée. Un volume suffisant de données doit être mis à disposition du composable afin qu'il dispose toujours de suffisamment de contenu pour n'importe quelle taille d'affichage, même si une partie du contenu n'est pas toujours utilisée.
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
En nous appuyant sur l'exemple de fiche (Card), notez que la description est toujours transmise à Card. Même si la description n'est utilisée que lorsque la largeur permet de l'afficher, Card exige toujours la description, quelle que soit la largeur disponible.
Le fait de toujours transmettre suffisamment de contenu simplifie les mises en page adaptatives en les rendant moins dynamiques et évite de déclencher des effets secondaires lors du basculement entre les tailles d'écran (ce qui peut se produire en raison d'un redimensionnement de fenêtre, d'un changement d'orientation ou du pliage et du dépliage d'un appareil).
Ce principe permet également de préserver l'état entre des modifications de mise en page. En hissant des informations qui peuvent ne pas être utilisées dans toutes les tailles d'affichage, vous pouvez conserver l'état de l'application à mesure que la taille de la mise en page change.
Par exemple, vous pouvez hisser un indicateur booléen showMore afin de préserver l'état de l'application lorsque le redimensionnement de l'écran entraîne le masquage ou l'affichage du contenu par la mise en page :
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
En savoir plus
Pour en savoir plus sur les mises en page adaptatives dans Compose, consultez les ressources suivantes :
Applications exemples
- CanonicalLayouts est un dépôt de modèles de conception éprouvés qui offrent une expérience utilisateur optimale sur les grands écrans.
- JetNews : montre comment concevoir une application qui adapte son interface utilisateur afin d'utiliser l'espace d'affichage disponible.
- Reply est un exemple adaptatif pour la prise en charge des mobiles, des tablettes et des appareils pliables.
- Now in Android est une application qui utilise des mises en page adaptatives pour prendre en charge différentes tailles d'écran.
Vidéos
- Build Android UIs for any screen size (Créer des interfaces utilisateur Android pour toutes les tailles d'écran)
- Facteurs de forme | Sommet des développeurs Android 2022