Cette page explique comment gérer les tailles et fournir des mises en page flexibles et responsives avec Glance, à l'aide des composants Glance existants.
Utiliser Box
, Column
et Row
Glance propose trois mises en page principales de composables:
Box
: place les éléments les uns au-dessus des autres. Il est traduit par unRelativeLayout
.Column
: place les éléments les uns après les autres sur l'axe vertical. Il se traduit par unLinearLayout
à orientation verticale.Row
: place les éléments les uns après les autres sur l'axe horizontal. Il se traduit par unLinearLayout
à orientation horizontale.
Glance est compatible avec les objets Scaffold
. Placez vos composables Column
, Row
et Box
dans un objet Scaffold
donné.
Chacun de ces composables vous permet de définir les alignements verticaux et horizontaux de son contenu, ainsi que les contraintes de largeur, de hauteur, de poids ou de marge intérieure à l'aide de modificateurs. De plus, chaque enfant peut définir son modificateur pour modifier l'espace et le positionnement dans le parent.
L'exemple suivant montre comment créer un Row
qui distribue ses enfants de manière uniforme horizontalement, comme illustré dans la figure 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
remplit la largeur maximale disponible. Étant donné que chaque enfant a le même poids, ils partagent l'espace disponible de manière égale. Vous pouvez définir différentes épaisseurs, tailles, marges intérieures ou alignements pour adapter les mises en page à vos besoins.
Utiliser des mises en page à défilement
Une autre façon de fournir du contenu responsif consiste à le rendre déroulant. Vous pouvez pour cela utiliser le composable LazyColumn
. Ce composable vous permet de définir un ensemble d'éléments à afficher dans un conteneur à faire défiler dans le widget de l'application.
Les extraits de code suivants présentent différentes manières de définir des éléments dans LazyColumn
.
Vous pouvez indiquer le nombre d'articles:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Fournissez des éléments individuels:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Fournissez une liste ou un tableau d'éléments:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
Vous pouvez également combiner les exemples précédents:
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
Notez que l'extrait de code précédent ne spécifie pas itemId
. Spécifier itemId
permet d'améliorer les performances et de maintenir la position de défilement via les mises à jour de la liste et de appWidget
à partir d'Android 12 (par exemple, lors de l'ajout ou de la suppression d'éléments de la liste). L'exemple suivant montre comment spécifier un itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
Définir le SizeMode
Les tailles AppWidget
peuvent varier en fonction de l'appareil, du choix de l'utilisateur ou du lanceur. Il est donc important de fournir des mises en page flexibles, comme décrit sur la page Fournir des mises en page de widgets flexibles. Glance simplifie ce processus avec la définition SizeMode
et la valeur LocalSize
. Les sections suivantes décrivent les trois modes.
SizeMode.Single
SizeMode.Single
est le mode par défaut. Il indique qu'un seul type de contenu est fourni. Autrement dit, même si la taille disponible de AppWidget
change, la taille du contenu ne change pas.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
Lorsque vous utilisez ce mode, assurez-vous que:
- Les valeurs de métadonnées de taille minimale et maximale sont correctement définies en fonction de la taille du contenu.
- Le contenu est suffisamment flexible dans la plage de taille attendue.
En règle générale, utilisez ce mode dans les cas suivants:
a) le AppWidget
a une taille fixe ; ou b) son contenu ne change pas lors de la redimensionnement.
SizeMode.Responsive
Ce mode équivaut à fournir des mises en page responsives, ce qui permet à GlanceAppWidget
de définir un ensemble de mises en page responsives limitées par des tailles spécifiques. Pour chaque taille définie, le contenu est créé et mappé à la taille spécifique lors de la création ou de la mise à jour de l'AppWidget
. Le système sélectionne ensuite la meilleure correspondance en fonction de la taille disponible.
Par exemple, dans notre destination AppWidget
, vous pouvez définir trois tailles et son contenu:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
Dans l'exemple précédent, la méthode provideContent
est appelée trois fois et mappée sur la taille définie.
- Dans le premier appel, la taille est évaluée à
100x100
. Le contenu n'inclut pas le bouton supplémentaire, ni les textes en haut et en bas. - Dans le deuxième appel, la taille est évaluée à
250x100
. Le contenu inclut le bouton supplémentaire, mais pas les textes en haut et en bas. - Dans le troisième appel, la taille est évaluée à
250x250
. Le contenu inclut le bouton supplémentaire et les deux textes.
SizeMode.Responsive
est une combinaison des deux autres modes et vous permet de définir du contenu responsif dans des limites prédéfinies. En général, ce mode est plus performant et permet des transitions plus fluides lorsque la AppWidget
est redimensionnée.
Le tableau suivant indique la valeur de la taille, en fonction de la taille disponible de SizeMode
et de AppWidget
:
Taille disponible | 105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
---|---|---|---|---|
SizeMode.Single |
110 x 110 | 110 x 110 | 110 x 110 | 110 x 110 |
SizeMode.Exact |
105 x 110 | 203 x 112 | 72 x 72 | 203 x 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* Les valeurs exactes ne sont fournies qu'à des fins de démonstration. |
SizeMode.Exact
SizeMode.Exact
équivaut à fournir des mises en page exactes, ce qui demande le contenu GlanceAppWidget
chaque fois que la taille AppWidget
disponible change (par exemple, lorsque l'utilisateur redimensionne le AppWidget
sur l'écran d'accueil).
Par exemple, dans le widget de destination, un bouton supplémentaire peut être ajouté si la largeur disponible est supérieure à une certaine valeur.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
Ce mode offre plus de flexibilité que les autres, mais présente quelques mises en garde:
AppWidget
doit être entièrement recréée chaque fois que la taille change. Cela peut entraîner des problèmes de performances et des sauts dans l'interface utilisateur lorsque le contenu est complexe.- La taille disponible peut varier en fonction de l'implémentation du lanceur. Par exemple, si le lanceur d'applications ne fournit pas la liste des tailles, la taille minimale possible est utilisée.
- Sur les appareils antérieurs à Android 12, la logique de calcul de la taille peut ne pas fonctionner dans toutes les situations.
En règle générale, vous devez utiliser ce mode si SizeMode.Responsive
ne peut pas être utilisé (c'est-à-dire qu'un petit ensemble de mises en page responsives n'est pas possible).
Accéder aux ressources
Utilisez LocalContext.current
pour accéder à n'importe quelle ressource Android, comme illustré dans l'exemple suivant:
LocalContext.current.getString(R.string.glance_title)
Nous vous recommandons de fournir directement des ID de ressource pour réduire la taille de l'objet RemoteViews
final et pour activer les ressources dynamiques, telles que les couleurs dynamiques.
Les composables et les méthodes acceptent les ressources à l'aide d'un "fournisseur", tel que ImageProvider
, ou à l'aide d'une méthode de surcharge telle que GlanceModifier.background(R.color.blue)
. Exemple :
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Gérer le texte
Glance 1.1.0 inclut une API permettant de définir vos styles de texte. Définissez des styles de texte à l'aide des attributs fontSize
, fontWeight
ou fontFamily
de la classe TextStyle.
fontFamily
est compatible avec toutes les polices système, comme illustré dans l'exemple suivant, mais les polices personnalisées dans les applications ne sont pas prises en charge:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Ajouter des boutons composés
Les boutons composés ont été introduits dans Android 12. Glance est compatible avec les types de boutons composés suivants:
Ces boutons composés affichent chacun une vue cliquable qui représente l'état "coché".
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
Lorsque l'état change, le lambda fourni est déclenché. Vous pouvez stocker l'état de la vérification, comme illustré dans l'exemple suivant:
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
Vous pouvez également fournir l'attribut colors
à CheckBox
, Switch
et RadioButton
pour personnaliser leurs couleurs:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )
Composants supplémentaires
Glance 1.1.0 inclut la publication de composants supplémentaires, comme décrit dans le tableau suivant:
Nom | Image | Lien de référence | Remarques supplémentaires |
---|---|---|---|
Bouton rempli | Composant | ||
Boutons avec contours | Composant | ||
Boutons d'icône | Composant | Primaire / Secondaire / Icône uniquement | |
Barre de titre | Composant | ||
Scaffold | La structure en échafaudage et la barre de titre se trouvent dans la même démonstration. |
Pour en savoir plus sur les spécificités de la conception, consultez les conceptions de composants dans ce kit de conception sur Figma.
Pour en savoir plus sur les mises en page canoniques, consultez Mises en page de widgets canoniques.