Une fonction composable peut contenir divers éléments. Sans instructions précises sur leur disposition, Jetpack Compose les empile les uns sur les autres. Voyons donc comment les agencer en fonction de nos besoins. Jetpack Compose dispose de trois layouts principaux :Row
,Column
etBox
. Ces layouts permettent de positionner les éléments respectivement de manière horizontale, verticale, en superposition simple ou en superposition selon l’espace disponible.
Disposez vos composants en ligne
Découvrez sa signature
Le layout Row
permet d'aligner plusieurs composants de manière horizontale. Voici sa signature :
@Composable
fun Row(
modifier: Modifier = Modifier,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top,
content: @Composable RowScope.() -> Unit
)
Concentrons-nous sur les paramètres horizontalArrangement
etverticalAlignment
, qui permettent de modifier l'agencement des enfants d'un layout de typeRow
.
Découvez le paramètre horizontalArrangement
Il spécifie la disposition horizontale des enfants. Il existe plusieurs valeurs prédéfinies dans l'objetArrangement.Horizontal
, telles que :Start
,End
,Center
,SpaceBetween
,SpaceAround
,SpaceEvenly
, ainsi que la fonctionspacedBy()
.
Découvez le paramètre verticalAlignment
Il détermine l'alignement vertical des enfants. Les valeurs prédéfinies dans l'objetAlignment.Vertical
incluent :Top
,Bottom
etCenterVertically
.
Comment cela s’applique à notre application, BestPodcast ?
Les podcasts de notre application peuvent être divisés en plusieurs catégories. Par exemple : Technologie, Écologie et Santé. Illustrons alors comment utiliser le layoutRow
en regardant l’implémentation du composantCategoryFilter
pour filtrer nos podcasts. Voici le code à écrire :
@Composable
fun CategoryFilter(
categories: List<Category>,
selectedId: Int?,
onCategoryClicked: (Int) -> Unit,
) {
Row(
modifier = Modifier.fillMaxWidth().selectableGroup(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
categories.forEach { category ->
val isSelected = (category.id == selectedId)
Button(
modifier =
Modifier.selectable(
selected = isSelected,
onClick = {
onCategoryClicked(category.id)
},
),
onClick = { onCategoryClicked(category.id) },
colors =
ButtonDefaults.buttonColors(
containerColor = if (isSelected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.secondaryContainer
},
contentColor = if (isSelected) {
MaterialTheme.colorScheme.onPrimary
} else {
MaterialTheme.colorScheme.onSecondaryContainer
},
),
) {
if (isSelected) {
Icon(
Icons.Default.Check,
contentDescription = null,
)
}
Text(text = category.text)
}
}
}
}
Le composantCategoryFilter
utilise uneRow
pour afficher les boutons horizontalement, avec un espacement égal entre chaque bouton grâce au paramètrehorizontalArrangement
et à sa valeurArrangement.SpaceEvenly
.Vous pouvez récupérer le code de ce composant sur GitHub, si vous souhaitez manipuler la propriétéhorizontalArrangement
afin de visualiser l’impact sur l’arrangement des boutons.
Et voici à quoi ressemble le composant final :
Voici une vidéo qui récapitule les principales étapes pour disposer en ligne le composantCategoryFilter
de l’application “Bestpodcasts”.
Disposez vos composants en colonne
Découvrez sa signature
Le layoutColumn
permet d'aligner plusieurs composants verticalement. Sa signature est la suivante :
@Composable
fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
)
Comme pour le layoutRow
, deux paramètres permettent de spécifier la disposition des enfants verticalement ou horizontalement. Ces paramètres organisent uniformément les composants à l'intérieur d'un layout de typeColumn
.
Découvrez le paramètreverticalArrangement
Il définit la disposition verticale des enfants. Les valeurs prédéfinies incluentTop
,Bottom
,Center
,SpaceBetween
,SpaceAround
,SpaceEvenly
, ainsi que la fonctionspacedBy()
.
Découvrez le paramètrehorizontalAlignement
Il définit l'alignement horizontal des enfants. Les valeurs prédéfinies sont :Start
,End
,CenterHorizontally
.
Comment cela s’applique à notre application, BestPodcast ?
Imaginons que l’on souhaite afficher les catégories de nos podcasts sous forme de colonne. Si on souhaite les espacer équitablement verticalement et les centrer horizontalement, le code donnerait alors :
@Composable
fun CategoryFilter(
categories: List<Category>,
selectedId: Int?,
onCategoryClicked: (Int) -> Unit,
) {
Column(
modifier = Modifier.fillMaxWidth().selectableGroup(),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
categories.forEach { category ->
/*...*/
}
}
}
Le rendu sera alors semblable à ceci :
Superposez vos composants
Découvrez la signature du layoutBox
L’objectif du layoutBox
est de superposer des composants. Voici sa signature :
fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: @Composable BoxScope.() -> Unit
) {
/*...*/
}
Découvrez le paramètrecontentAlignement
Il sert à spécifier l'alignement commun à tous les éléments enfants de ce layout. Il existe plusieurs valeurs prédéfinies dans l'objetAlignment
, notamment :Center
,CenterStart
,CenterEnd
,TopStart
,TopCenter
,TopEnd
,BottomStart
,BottolCenter
,BottomEnd
.
Comment cela s’applique à notre application, BestPodcast ?
Prenons comme exemple le composant ci-dessous, qui consiste en la superposition du logo d’un podcast, avec une image statique représentant une onde sonore. Nous appellerons ce composantHighlightedLogo
. Pour réaliser ce composant, l’usage du layoutBox
est nécessaire. Le code de ce composant est le suivant :
@Composable
private fun HighlightedLogo(
logoUrl: String,
contentDescription: String?,
) {
Box(
contentAlignment = Alignment.Center,
) {
Image(
painter = painterResource(id = R.drawable.soundwave),
contentDescription = null,
colorFilter =
ColorFilter.tint(
color = MaterialTheme.colorScheme.primary,
),
modifier = Modifier.fillMaxWidth(),
)
AsyncImage(
model = logoUrl,
contentDescription = contentDescription,
placeholder = painterResource(id = R.drawable.placeholder),
modifier =
Modifier
.fillMaxWidth(fraction = 0.6f)
.aspectRatio(1f)
.padding(8.dp),
)
}
}
En analysant ce code, vous devriez remarquer les points suivants :
Les enfants du layout
Box
se superposent dans l’ordre dans lequel ils sont écrits dans le code. Ainsi, si vous inversez l’ordre en mettant le composantAsyncImage
du logo avant l’imagesoundwave
, alors le logo sera partiellement masqué par l’onde.L’usage du paramètre
contentAlignment
avec la valeurAlignment.Center
, permet de centrer les éléments.
Voici le résultat.
Voici une vidéo qui récapitule les principales étapes pour supperposer vos composants.
À vous de jouer !
Contexte
Il est grand temps de peaufiner l’agencement des éléments constituant notre composantPodcastItem
, puis d’intégrer la notion de catégorie de podcast que nous venons de voir. Voici les consignes que Charlie vous a transmises.
Consignes
Modifiez le composant
PodcastItem
afin de centrer verticalement les éléments qu’il contient.Espacez chaque élément du composant
PodcastItem
de8.dp
.Intégrez la notion de catégorie au sein de l’objet
Podcast
et de laPodcastFactory
. Vous pouvez pour cela récupérer le code sur GitHub.Affichez la catégorie sous le titre du podcast, en utilisant le bon layout. Le texte de la catégorie doit être de la couleur
tertiary
de votre thème, écrit en italique et avec le stylelabelSmall
.Superposez au logo du podcast une icône permettant d’indiquer visuellement qu’il s’agit d’un podcast avec les spécifications suivantes :
Utiliser l’icône Material
Icons.Default.Podcasts
.Celle-ci doit faire
24.dp
en hauteur et largeur.Elle doit être positionnée en bas à droite du logo.
Elle doit avoir une teinte et une couleur de fond issue du thème Material.
Son bord inférieur droit doit être arrondi, pour épouser la forme du logo (utilisez pour cela le Modifier
clip
et la classeRoundedCornerShape
).
Livrables
Votre projet sur Android Studio doit être conforme aux consignes ci-dessus.
En résumé
Les layouts principaux de Jetpack Compose sont
Row
,Column
etBox
.Row
permet d’organiser globalement ses enfants horizontalement et verticalement grâce aux paramètreshorizontalArrangement
etverticalAlignment
.Column
permet d’organiser globalement ses enfants verticalement et horizontalement grâce aux paramètresverticalArrangement
ethorizontalAlignment
.Box
permet d’aligner globalement ses enfants grâce au paramètrecontentAlignment
.
Nous avons appris à structurer nos composants grâce aux layoutsRow
,Column
etBox
. Nous avons également vu comment organiser et aligner leurs enfants de manière globale. Il est maintenant temps d’aller un peu plus loin en optimisant et améliorant l’organisation de ces composants.