1. Introduction
L'un des principaux avantages liés au développement de votre application sur la plate-forme Android est que vous pouvez toucher les utilisateurs dans différents formats (accessoires connectés, appareils pliables, tablettes, ordinateurs et télévision). Ils peuvent avoir besoin d'utiliser la même application sur des appareils à grand écran pour bénéficier d'un espace plus important. De plus en plus d'utilisateurs Android se servent de leurs applications sur divers appareils avec des tailles d'écran différentes et s'attendent à une expérience utilisateur de haute qualité sur tous ces appareils.
Jusqu'à présent, vous avez appris à créer des applications principalement destinées aux appareils mobiles. Dans cet atelier de programmation, vous découvrirez comment transformer vos applications pour qu'elles s'adaptent à d'autres tailles d'écran. Vous aurez recours à des modèles de mise en page conçus pour la navigation adaptative. Ces modèles sont attrayants et peuvent être utilisés à la fois pour les appareils mobiles et les grands écrans, tels que les pliables, les tablettes et les ordinateurs.
Conditions préalables
- Vous maîtrisez la programmation Kotlin, y compris les classes, les fonctions et les conditions.
- Vous maîtrisez l'utilisation des classes
ViewModel
. - Vous maîtrisez la création des fonctions
Composables
. - Vous savez créer des mises en page avec Jetpack Compose.
- Vous savez exécuter des applications sur un appareil ou un émulateur.
Points abordés
- Créer une navigation entre les écrans sans graphique de navigation pour les applications simples
- Créer une mise en page de navigation adaptative avec Jetpack Compose
- Créer un gestionnaire de retour personnalisé
Objectifs de l'atelier
- Vous implémenterez la navigation dynamique dans l'application Reply existante afin d'adapter sa mise en page à toutes les tailles d'écran
Le produit fini ressemblera à l'image ci-dessous :
Ce dont vous avez besoin
- Un ordinateur avec accès à Internet, un navigateur Web et Android Studio
- Accès à GitHub
2. Présentation de l'application
Présentation de l'application Reply
L'application Reply est une application multi-écran semblable à un client de messagerie.
Elle comprend quatre catégories différentes qui sont affichées dans différents onglets : "Boîte de réception", "Messages envoyés" "Brouillons" et "Spam".
Télécharger le code de démarrage
Dans Android Studio, ouvrez le dossier basic-android-kotlin-compose-training-reply-app
.
- Accédez à la page du dépôt GitHub fournie pour le projet.
- Vérifiez que le nom de la branche correspond à celui spécifié dans l'atelier de programmation. Par exemple, dans la capture d'écran suivante, le nom de la branche est main.
- Sur la page GitHub du projet, cliquez sur le bouton Code pour afficher une fenêtre pop-up.
- Dans la fenêtre pop-up, cliquez sur le bouton Télécharger le fichier ZIP pour enregistrer le projet sur votre ordinateur. Attendez la fin du téléchargement.
- Recherchez le fichier sur votre ordinateur (il se trouve probablement dans le dossier Téléchargements).
- Double-cliquez sur le fichier ZIP pour le décompresser. Un dossier contenant les fichiers du projet est alors créé.
Ouvrir le projet dans Android Studio
- Lancez Android Studio.
- Dans la fenêtre Bienvenue dans Android Studio, cliquez sur Ouvrir.
Remarque : Si Android Studio est déjà ouvert, sélectionnez l'option de menu Fichier > Ouvrir.
- Dans l'explorateur de fichiers, accédez à l'emplacement du dossier du projet décompressé (il se trouve probablement dans le dossier Téléchargements).
- Double-cliquez sur le dossier de ce projet.
- Attendez qu'Android Studio ouvre le projet.
- Cliquez sur le bouton Exécuter pour compiler et exécuter l'application. Assurez-vous que tout fonctionne comme prévu.
3. Tutoriel du code de démarrage
Répertoires importants de l'application Reply
La couche de données et la couche d'interface utilisateur du projet d'application Reply sont divisées en différents répertoires. ReplyViewModel
, ReplyUiState
et les autres composables se trouvent dans le répertoire ui
. Les classes data
et enum
qui définissent la couche de données et les classes du fournisseur de données se trouvent dans le répertoire data
.
Initialisation des données dans l'application Reply
L'application Reply est initialisée avec les données via la méthode initilizeUIState()
dans ReplyViewModel
, qui est exécutée dans la fonction init
.
ReplyViewModel.kt
...
init {
initializeUIState()
}
private fun initializeUIState() {
var mailboxes: Map<MailboxType, List<Email>> =
LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
_uiState.value =
ReplyUiState(
mailboxes = mailboxes,
currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
?: LocalEmailsDataProvider.defaultEmail
)
}
...
Composable au niveau de l'écran
Comme pour les autres applications, l'application Reply utilise le composable ReplyApp
comme composable principal, où les éléments viewModel
et uiState
sont déclarés. Différentes fonctions viewModel
sont également transmises en tant qu'arguments lambda pour le composable ReplyHomeScreen
.
ReplyApp.kt
...
@Composable
fun ReplyApp(modifier: Modifier = Modifier) {
val viewModel: ReplyViewModel = viewModel()
val replyUiState = viewModel.uiState.collectAsState().value
ReplyHomeScreen(
replyUiState = replyUiState,
onTabPressed = { mailboxType: MailboxType ->
viewModel.updateCurrentMailbox(mailboxType = mailboxType)
viewModel.resetHomeScreenStates()
},
onEmailCardPressed = { email: Email ->
viewModel.updateDetailsScreenStates(
email = email
)
},
onDetailScreenBackPressed = {
viewModel.resetHomeScreenStates()
},
modifier = modifier
)
}
Autres composables
ReplyHomeScreen.kt
: contient les composables de l'écran d'accueil, y compris les éléments de navigation.ReplyHomeContent.kt
: contient des composables qui définissent des composables plus détaillés de l'écran d'accueil.ReplyDetailsScreen.kt
: contient des composables d'écran et des composables de plus petite taille pour l'écran d'informations.
N'hésitez pas à examiner chaque fichier en détail pour vous familiariser avec les composables avant de passer à la section suivante de l'atelier de programmation.
4. Changer d'écran sans graphique de navigation
Dans le parcours précédent, vous avez appris à utiliser une classe NavHostController
pour passer d'un écran à un autre. Avec Compose, vous pouvez également changer d'écran avec des instructions conditionnelles simples à l'aide d'états d'exécution modifiables. Cette fonctionnalité est particulièrement utile dans les petites applications telles que Reply, qui nécessitent uniquement de basculer entre deux écrans.
Changer d'écran via les changements d'état
Dans Compose, les écrans sont recomposés lorsqu'un changement d'état se produit. Vous pouvez modifier les écrans à l'aide de conditions simples pour répondre aux changements d'état.
Vous allez utiliserez des expressions conditionnelles pour afficher le contenu de l'écran d'accueil lorsque l'utilisateur s'y trouve, et l'écran de détails lorsque l'utilisateur n'est pas sur l'écran d'accueil.
Pour permettre à l'application Reply de changer d'écran en cas de changement d'état, procédez comme suit :
- Ouvrez le code de démarrage dans Android Studio.
- Dans le composable
ReplyHomeScreen
dansReplyHomeScreen.kt
, encapsulez le composableReplyAppContent
avec une instructionif
lorsque la propriétéisShowingHomepage
de l'objetreplyUiState
correspond àtrue
.
ReplyHomeScreen.kt
@Composable
fun ReplyHomeScreen(
replyUiState: ReplyUiState,
onTabPressed: (MailboxType) -> Unit = {},
onEmailCardPressed: (Int) -> Unit = {},
onDetailScreenBackPressed: () -> Unit = {},
modifier: Modifier = Modifier
) {
...
if (replyUiState.isShowingHomepage) {
ReplyAppContent(
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
}
}
Vous devez désormais tenir compte du scénario où l'utilisateur n'est pas sur l'écran d'accueil, et afficher dans ce cas l'écran des détails.
- Ajoutez une branche
else
avec le composableReplyDetailsScreen
dans le corps. AjoutezreplyUIState
,onDetailScreenBackPressed
etmodifier
comme arguments pour le composableReplyDetailsScreen
.
ReplyHomeScreen.kt
@Composable
fun ReplyHomeScreen(
replyUiState: ReplyUiState,
onTabPressed: (MailboxType) -> Unit = {},
onEmailCardPressed: (Int) -> Unit = {},
onDetailScreenBackPressed: () -> Unit = {},
modifier: Modifier = Modifier
) {
...
if (replyUiState.isShowingHomepage) {
ReplyAppContent(
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
} else {
ReplyDetailsScreen(
replyUiState = replyUiState,
onBackPressed = onDetailScreenBackPressed,
modifier = modifier
)
}
}
L'objet replyUiState
est un objet d'état. Par conséquent, en cas de modification de la propriété isShowingHomepage
correspondant à l'objet replyUiState
, le composable ReplyHomeScreen
est recomposé, et l'instruction if/else
est réévaluée au moment de l'exécution. Cette approche permet de naviguer entre différents écrans sans avoir à utiliser de classe NavHostController
.
Créer un gestionnaire de retour personnalisé
L'un des avantages du composable NavHost
pour changer d'écran est que la direction des écrans précédents est enregistrée dans la pile arrière. Ces écrans enregistrés permettent au bouton "Retour" du système de revenir facilement à l'écran précédent lorsqu'il est appelé. Étant donné que l'application Reply n'utilise pas de NavHost
, vous devez ajouter le code pour gérer le bouton "Retour" manuellement. C'est ce que vous allez faire dans un instant.
Pour créer un gestionnaire de retour personnalisé dans l'application Reply, procédez comme suit :
- Sur la première ligne du composable
ReplyDetailsScreen
, ajoutez un composableBackHandler
. - Appelez la fonction
onBackPressed()
dans le corps du composableBackHandler
.
ReplyDetailsScreen.kt
...
import androidx.activity.compose.BackHandler
...
@Composable
fun ReplyDetailsScreen(
replyUiState: ReplyUiState,
modifier: Modifier = Modifier,
onBackPressed: () -> Unit = {},
) {
BackHandler {
onBackPressed()
}
...
5. Exécuter l'application sur les appareils à grand écran
Vérifier votre application avec l'émulateur redimensionnable
Pour créer des applications vraiment utiles, les développeurs doivent comprendre l'expérience utilisateur attendue dans différents facteurs de forme. Par conséquent, il est important de tester les applications dans divers formats dès le début du processus de développement.
Pour atteindre cet objectif, il existe de nombreux émulateurs de différentes tailles. Cependant, cette opération peut s'avérer fastidieuse, en particulier lorsque vous créez plusieurs écrans à la fois. Vous devrez peut-être également tester la façon dont une application en cours d'exécution réagit aux changements de taille d'écran, tels que les changements d'orientation, les changements de taille de fenêtre sur un ordinateur et les changements d'état des appareils pliables.
Avec l'introduction de l'émulateur redimensionnable, Android Studio vous permet de tester ces scénarios.
Pour configurer l'émulateur redimensionnable, procédez comme suit :
- Assurez-vous d'exécuter Android Studio Chipmunk | 2021.2.1 ou version ultérieure.
- Dans Android Studio, sélectionnez Outils > Gestionnaire d'appareils.
- Dans Gestionnaire d'appareils, cliquez sur Créer un appareil.
- Sélectionnez la catégorie Téléphone et l'appareil Redimensionnable (expérimental).
- Cliquez sur Suivant.
- Sélectionnez Niveau d'API 33.
- Cliquez sur Suivant.
- Attribuez un nom au nouvel appareil virtuel Android.
- Cliquez sur Terminer.
Exécuter l'application sur un émulateur à grand écran
Maintenant que vous avez configuré l'émulateur redimensionnable, voyons ce à quoi ressemble l'application sur un grand écran.
- Exécutez l'application sur l'émulateur redimensionnable.
- Sélectionnez Tablette comme mode d'affichage.
- Inspectez l'application en mode Tablette et en mode Paysage.
Notez que l'écran de la tablette est allongé horizontalement. Bien que cette orientation fonctionne, elle ne convient pas forcément à une utilisation sur grand écran. Voyons maintenant comment résoudre ce problème.
Conception pour les grands écrans
Lorsque l'on regarde cette application sur une tablette, on ne peut s'empêcher de penser que son design est médiocre et qu'elle n'est pas très attrayante. Le fait est que cette mise en page n'est pas adaptée aux grands écrans.
Lorsque vous concevez une application destinée à un affichage sur grand écran (tablettes et pliables, par exemple), vous devez tenir compte de l'ergonomie et de la proximité des doigts de l'utilisateur sur l'écran. Avec les appareils mobiles, les doigts des utilisateurs ont facilement accès à la majeure partie de l'écran. L'emplacement des éléments interactifs, tels que les boutons et les éléments de navigation, n'est pas déterminant. Toutefois, pour les grands écrans, placer des éléments interactifs essentiels au milieu de l'écran peut nuire à leur accessibilité.
Comme vous pouvez le voir dans l'application Reply, la conception pour les grands écrans ne se limite pas à allonger ou à agrandir les éléments de l'interface utilisateur. Profitez de l'espace supplémentaire dont vous disposez pour créer une expérience différente pour vos utilisateurs. Par exemple, vous pouvez ajouter une autre mise en page sur le même écran pour éviter d'avoir à passer à un autre écran ou pour permettre le mode multitâche.
Cette conception peut accroître la productivité des utilisateurs et stimuler l'engagement. Avant de déployer cette conception, vous devez d'abord apprendre à créer différentes mises en page pour différentes tailles d'écran.
6. Adapter la mise en page à différentes tailles d'écran
Qu'est-ce qu'un point d'arrêt ?
Vous vous demandez peut-être comment afficher différentes mises en page pour la même application. La réponse courte consiste à utiliser des conditions au niveau de différents états, comme vous l'avez fait au début de cet atelier de programmation.
Pour créer une application adaptative, vous devez modifier la mise en page en fonction de la taille de l'écran. Le point de mesure où la mise en page change s'appelle "point d'arrêt". Material Design a créé une plage de points d'arrêt adaptée à la plupart des écrans Android.
Ce tableau des plages de points d'arrêt indique, par exemple, que si votre application est en cours d'exécution sur un appareil dont la taille d'écran est inférieure à 600 dp, vous devez afficher la mise en page mobile.
Utiliser des classes de taille de fenêtre
L'API WindowSizeClass
créée pour Compose facilite l'implémentation des points d'arrêt Material Design.
Les classes de taille de fenêtre présentent trois catégories de tailles : compacte, moyenne et grande, à la fois pour la largeur et la hauteur.
Pour implémenter l'API WindowSizeClass
dans l'application Reply, procédez comme suit :
- Ajoutez la dépendance
material3-window-size-class
au fichierbuild.gradle
du module.
build.gradle
...
dependencies {
...
"androidx.compose.material3:material3-window-size-class:$material3_version"
...
- Cliquez sur Synchroniser maintenant pour synchroniser Gradle après avoir ajouté la dépendance.
Maintenant que le fichier build.grade
est à jour, vous pouvez créer une variable qui stockera la taille de la fenêtre de l'application à tout moment.
- Dans la fonction
onCreate()
du fichierMainActivity.kt
, attribuez la méthodecalculateWindowSizeClass
avec le contextethis
transmis dans le paramètre à une variable nomméewindowSize
. - Importez le package
calculateWindowSizeClass
approprié.
MainActivity.kt
...
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ReplyTheme {
val windowSize = calculateWindowSizeClass(this)
ReplyApp()
...
- Notez la couleur rouge de la syntaxe
calculateWindowSizeClass
, qui entraîne l'affichage de l'ampoule rouge. Cliquez sur cette ampoule à gauche de la variablewindowSize
, puis sélectionnez Activer 'ExperimentalMaterial3WindowSizeClassApi' sur 'onCreate' pour créer une annotation au-dessus de la méthodeonCreate()
.
Vous pouvez utiliser la variable WindowWidthSizeClass
dans MainActivity.kt
pour déterminer la mise en page à afficher dans différents composables. Préparons le composable ReplyApp
à recevoir cette valeur.
- Dans le fichier
ReplyApp.kt
, modifiez le composableReplyApp
pour accepterWindowWidthSizeClass
en tant que paramètre, puis importez le package approprié.
ReplyApp.kt
...
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
...
@Composable
fun ReplyApp(
windowSize: WindowWidthSizeClass,
modifier: Modifier = Modifier
) {
...
- Transmettez la variable
windowSize
au composantReplyApp
dans la méthodeonCreate()
du fichierMainActivity.kt
.
MainActivity.kt
...
setContent {
ReplyTheme {
val windowSize = calculateWindowSizeClass(this)
ReplyApp(
windowSize = windowSize.widthSizeClass
)
...
Vous devez également mettre à jour l'aperçu de l'application pour le paramètre windowSize
.
- Transmettez
WindowWidthSizeClass.Compact
en tant que paramètrewindowSize
au composableReplyApp
pour le composant d'aperçu, puis importez le package approprié.
MainActivity.kt
...
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
...
@Preview(showBackground = true)
@Composable
fun ReplyAppPreview() {
ReplyTheme {
ReplyApp(
windowSize = WindowWidthSizeClass.Compact,
)
}
}
- Pour modifier la mise en page de l'application en fonction de la taille de l'écran, ajoutez une instruction
when
dans le composableReplyApp
en fonction de la valeurWindowWidthSizeClass
.
ReplyApp.kt
...
@Composable
fun ReplyApp(
windowSize: WindowWidthSizeClass,
modifier: Modifier = Modifier
) {
val viewModel: ReplyViewModel = viewModel()
val replyUiState = viewModel.uiState.collectAsState().value
when (windowSize) {
WindowWidthSizeClass.Compact -> {
}
WindowWidthSizeClass.Medium -> {
}
WindowWidthSizeClass.Expanded -> {
}
else -> {
}
}
...
À ce stade, vous avez établi une base pour utiliser les valeurs WindowSizeClass
afin de modifier la mise en page dans votre application. L'étape suivante consiste à déterminer l'apparence de votre application sur différentes tailles d'écran.
7. Implémenter une mise en page pour la navigation adaptative
Implémenter la navigation adaptative dans l'interface utilisateur
Actuellement, la navigation en bas de l'écran est utilisée pour toutes les tailles d'écran.
Comme nous l'avons vu précédemment, cette mise en page n'est pas idéale, car il est difficile pour les utilisateurs d'accéder à ces éléments de navigation essentiels sur les grands écrans. Heureusement, vous trouverez des recommandations pour l'affichage des différents éléments de navigation en fonction de différentes classes de taille de fenêtre dans la section Navigation pour les interfaces utilisateur responsives. Pour l'application Reply, vous pouvez implémenter les éléments suivants :
Le rail de navigation est un autre composant de navigation conçu par Material Design. Il permet d'accéder à des options de navigation compactes pour les destinations principales depuis l'application.
De même, un panneau de navigation persistant/permanent a été créé par Material Design pour assurer un accès ergonomique sur les grands écrans.
Implémenter un panneau de navigation
Si vous souhaitez créer un panneau de navigation pour les écrans étendus, vous pouvez utiliser le paramètre navigationType
. Pour ce faire, procédez comme suit :
- Pour représenter différents types d'éléments de navigation, créez un fichier
WindowStateUtils.kt
dans un nouveau packageutils
, qui se trouve sous le répertoireui
. - Ajoutez une classe
Enum
pour représenter différents types d'éléments de navigation.
WindowStateUtils.kt
package com.example.reply.ui.utils
enum class ReplyNavigationType {
BOTTOM_NAVIGATION, NAVIGATION_RAIL, PERMANENT_NAVIGATION_DRAWER
}
Pour implémenter correctement le panneau de navigation, vous devez déterminer le type de navigation en fonction de la taille de la fenêtre de l'application.
- Dans le composable
ReplyApp
, créez une variablenavigationType
et attribuez-lui la valeurReplyNavigationType
appropriée, en fonction de la taille de l'écran indiquée dans l'instructionwhen
.
ReplyApp.kt
...
import com.example.reply.ui.utils.ReplyNavigationType
...
when (windowSize) {
WindowWidthSizeClass.Compact -> {
navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
}
WindowWidthSizeClass.Medium -> {
navigationType = ReplyNavigationType.NAVIGATION_RAIL
}
WindowWidthSizeClass.Expanded -> {
navigationType = ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
}
else -> {
navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
}
}
...
Vous pouvez utiliser la valeur navigationType
dans le composable ReplyHomeScreen
. Pour ce faire, commencez par définir cette valeur comme paramètre pour le composable.
- Dans le composable
ReplyHomeScreen
, ajouteznavigationType
comme paramètre.
ReplyHomeScreen.kt
...
@Composable
fun ReplyHomeScreen(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: (MailboxType) -> Unit = {},
onEmailCardPressed: (Email) -> Unit = {},
onDetailScreenBackPressed: () -> Unit = {},
modifier: Modifier = Modifier
)
...
- Transmettez
navigationType
dans le composableReplyHomeScreen
.
ReplyApp.kt
...
ReplyHomeScreen(
navigationType = navigationType,
replyUiState = replyUiState,
onTabPressed = { mailboxType: MailboxType ->
viewModel.updateCurrentMailbox(mailboxType = mailboxType)
viewModel.resetHomeScreenStates()
},
onEmailCardPressed = { email: Email ->
viewModel.updateDetailsScreenStates(
email = email
)
},
onDetailScreenBackPressed = {
viewModel.resetHomeScreenStates()
},
modifier = modifier
)
...
Vous pouvez ensuite créer une branche pour présenter le contenu de l'application avec un panneau de navigation lorsque l'utilisateur ouvrira l'application sur un écran étendu et affichera l'écran d'accueil.
- Dans le corps du composable
ReplyHomeScreen
, ajoutez une instructionif
pour la conditionnavigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER && replyUiState.isShowingHomepage
.
ReplyHomeScreen.kt
import androidx.compose.material3.PermanentNavigationDrawer
...
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReplyHomeScreen(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: (MailboxType) -> Unit = {},
onEmailCardPressed: (Email) -> Unit = {},
onDetailScreenBackPressed: () -> Unit = {},
modifier: Modifier = Modifier
) {
...
if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
&& replyUiState.isShowingHomepage
) {
}
if (replyUiState.isShowingHomepage) {
ReplyAppContent(
replyUiState = replyUiState,
...
- Pour concevoir le panneau permanent, créez le composable
PermanentNavigationDrawer
dans le corps de l'instruction "if" et ajoutez le composableNavigationDrawerContent
en tant qu'entrée pour le paramètredrawerContent
. - Ajoutez le composable
ReplyAppContent
en tant qu'argument lambda final dePermanentNavigationDrawer
.
ReplyHomeScreen.kt
...
if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
&& replyUiState.isShowingHomepage
) {
PermanentNavigationDrawer(
drawerContent = {
NavigationDrawerContent(
selectedDestination = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
) {
ReplyAppContent(
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
}
}
...
- Ajoutez une branche
else
qui utilisera le corps de l'ancien composable afin de conserver l'ancienne branche pour les écrans non étendus.
ReplyHomeScreen.kt
...
if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
&& replyUiState.isShowingHomepage
) {
PermanentNavigationDrawer(
drawerContent = {
NavigationDrawerContent(
selectedDestination = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
) {
ReplyAppContent(
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
}
} else {
if (replyUiState.isShowingHomepage) {
ReplyAppContent(
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
} else {
ReplyDetailsScreen(
replyUiState = replyUiState,
onBackPressed = onDetailScreenBackPressed,
modifier = modifier
)
}
}
}
...
- Ajoutez une annotation "Expérimental" au composable
ReplyHomeScreen
. Cela est nécessaire, car l'APIPermanentNavigationDrawer
est encore en phase expérimentale.
ReplyHomeScreen.kt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReplyHomeScreen(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: (MailboxType) -> Unit = {},
onEmailCardPressed: (Email) -> Unit = {},
onDetailScreenBackPressed: () -> Unit = {},
modifier: Modifier = Modifier
) {
...
- Exécutez l'application en mode Tablette. L'écran suivant devrait s'afficher :
Implémenter un rail de navigation
Comme pour l'implémentation du panneau de navigation, vous devez utiliser le paramètre navigationType
pour passer d'un élément de navigation à un autre.
Commençons par ajouter un rail de navigation pour les écrans de taille moyenne.
- Commencez par préparer le composable
ReplyAppContent
en ajoutantnavigationType
comme paramètre.
ReplyHomeScreen.kt
...
@Composable
private fun ReplyAppContent(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: ((MailboxType) -> Unit) = {},
onEmailCardPressed: (Email) -> Unit = {},
navigationItemContentList: List<NavigationItemContent>,
modifier: Modifier = Modifier
) {
...
- Transmettez la valeur
navigationType
dans les deux composablesReplyAppContent
.
ReplyHomeScreen.kt
...
ReplyAppContent(
navigationType = navigationType,
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
}
} else {
if (replyUiState.isShowingHomepage) {
ReplyAppContent(
navigationType = navigationType,
replyUiState = replyUiState,
onTabPressed = onTabPressed,
onEmailCardPressed = onEmailCardPressed,
navigationItemContentList = navigationItemContentList,
modifier = modifier
)
...
Ajoutons à présent des branches, qui permettront à l'application d'afficher des rails de navigation dans certains cas.
- Dans la première ligne du corps du composable
ReplyAppContent
, encapsulez le composableReplyNavigationRail
autour du composableAnimatedVisibility
et définissez le paramètrevisibility
surtrue
si la valeurReplyNavigationType
correspond àNavigationRail
.
ReplyHomeScreen.kt
...
@Composable
private fun ReplyAppContent(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: ((MailboxType) -> Unit) = {},
onEmailCardPressed: (Email) -> Unit = {},
navigationItemContentList: List<NavigationItemContent>,
modifier: Modifier = Modifier
) {
AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) {
ReplyNavigationRail(
currentTab = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
Column(
modifier = Modifier
.fillMaxSize() .background(MaterialTheme.colorScheme.inverseOnSurface)
) {
ReplyListOnlyContent(
replyUiState = replyUiState,
onEmailCardPressed = onEmailCardPressed,
modifier = Modifier.weight(1f)
)
ReplyBottomNavigationBar(
currentTab = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
}
...
- Pour aligner correctement les composables, encapsulez le composable
AnimatedVisibility
et le composableColumn
qui se trouve dans le corps deReplyAppContent
dans un composableRow
.
ReplyHomeScreen.kt
...
@Composable
private fun ReplyAppContent(
navigationType: ReplyNavigationType,
replyUiState: ReplyUiState,
onTabPressed: ((MailboxType) -> Unit) = {},
onEmailCardPressed: (Email) -> Unit = {},
navigationItemContentList: List<NavigationItemContent>,
modifier: Modifier = Modifier
) {
Row(modifier = modifier.fillMaxSize()) {
AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) {
ReplyNavigationRail(
currentTab = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
Column(
modifier = Modifier
.fillMaxSize() .background(MaterialTheme.colorScheme.inverseOnSurface)
) {
ReplyListOnlyContent(
replyUiState = replyUiState,
onEmailCardPressed = onEmailCardPressed,
modifier = Modifier.weight(1f)
)
ReplyBottomNavigationBar(
currentTab = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
}
}
...
Enfin, vérifions que la barre de navigation inférieure s'affiche dans certains cas.
- Après le composable
ReplyListOnlyContent
, encapsulez le composableReplyBottomNavigationBar
avec un composableAnimatedVisibility
. - Définissez le paramètre
visible
lorsque la valeurReplyNavigationType
correspond àBOTTOM_NAVIGATION
.
ReplyHomeScreen.kt
...
ReplyListOnlyContent(
replyUiState = replyUiState,
onEmailCardPressed = onEmailCardPressed,
modifier = Modifier.weight(1f)
)
AnimatedVisibility(visible = navigationType == ReplyNavigationType.BOTTOM_NAVIGATION) {
ReplyBottomNavigationBar(
currentTab = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
...
- Exécutez l'application en mode Pliable non plié. L'écran suivant devrait s'afficher :
8. Télécharger le code de solution
Pour télécharger le code de l'atelier de programmation terminé, utilisez la commande git suivante :
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-reply-app.git cd basic-android-kotlin-compose-training-reply-app git checkout nav-update
Vous pouvez également télécharger le dépôt sous forme de fichier ZIP, le décompresser et l'ouvrir dans Android Studio.
Si vous souhaitez voir le code de solution, affichez-le sur GitHub.
9. Conclusion
Félicitations ! Vous êtes à deux doigts d'adapter l'application Reply à toutes les tailles d'écran avec une mise en page de navigation adaptative. Vous avez amélioré l'expérience utilisateur en proposant de nombreux formats Android. Dans le prochain atelier de programmation, vous renforcerez vos compétences en matière d'applications adaptatives en implémentant des mises en page, des tests et des aperçus adaptatifs.
N'oubliez pas de partager le fruit de vos efforts sur les réseaux sociaux avec le hashtag #AndroidBasics.