Questa pagina descrive come gestire le dimensioni e fornire layout flessibili e adattabili con la funzionalità Glance utilizzando i componenti Glance esistenti.
Utilizza Box
, Column
e Row
Il riepilogo ha tre layout componibili principali:
Box
: posiziona gli elementi sopra un altro. Viene tradotto inRelativeLayout
.Column
: posiziona gli elementi uno dopo l'altro sull'asse verticale. Traduci inLinearLayout
con orientamento verticale.Row
: posiziona gli elementi uno dopo l'altro sull'asse orizzontale. Traduci inLinearLayout
con orientamento orizzontale.
Riepilogo supporta gli oggetti Scaffold
. Posiziona gli elementi componibili Column
, Row
e
Box
all'interno di un determinato oggetto Scaffold
.
![Immagine di un layout di colonne, righe e caselle.](https://developer.android.com/static/develop/ui/compose/images/column_row_box.png?authuser=3&hl=it)
Ciascuno di questi elementi componibili ti consente di definire gli allineamenti verticali e orizzontali dei contenuti e i vincoli di larghezza, altezza, peso o spaziatura interna tramite i modificatori. Inoltre, ogni asset secondario può definire il proprio modificatore per cambiare lo spazio e il posizionamento all'interno dell'elemento principale.
L'esempio seguente mostra come creare un elemento Row
che distribuisca uniformemente gli elementi secondari orizzontalmente, come illustrato nella Figura 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
occupa la larghezza massima disponibile e, poiché ogni figlio ha lo stesso
peso, condivide in modo uniforme lo spazio disponibile. Puoi definire diversi pesi, dimensioni,
spaziature o allineamenti per adattare i layout alle tue esigenze.
Utilizza layout scorrevoli
Un altro modo per fornire contenuti adattabili è renderli scorrevoli. Ciò è
possibile con il componibile LazyColumn
. Questo componibile consente di definire un insieme di elementi da visualizzare all'interno di un contenitore scorrevole nel widget dell'app.
I seguenti snippet mostrano diversi modi per definire gli elementi all'interno di LazyColumn
.
Puoi specificare il numero di elementi:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Fornisci singoli elementi:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Fornisci un elenco o un array di elementi:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
Puoi anche utilizzare una combinazione degli esempi precedenti:
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") } }
Tieni presente che lo snippet precedente non specifica il itemId
. La specifica itemId
consente di migliorare il rendimento e mantenere la posizione di scorrimento dell'elenco e gli aggiornamenti di appWidget
a partire da Android 12 (ad esempio, quando si aggiungono o rimuovono elementi dall'elenco). L'esempio seguente mostra come specificare un itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
Definisci il SizeMode
Le dimensioni di AppWidget
possono variare a seconda del dispositivo, della scelta dell'utente o dell'Avvio app,
quindi è importante fornire layout flessibili come descritto nella pagina Fornire
layout flessibili dei widget. Il Riepilogo semplifica tutto ciò con la definizione
SizeMode
e il valore LocalSize
. Le seguenti sezioni descrivono
le tre modalità.
SizeMode.Single
SizeMode.Single
è la modalità predefinita. Indica che viene fornito un solo tipo di
contenuti, vale a dire che, anche se le dimensioni disponibili AppWidget
cambiano,
le dimensioni dei contenuti rimangono invariate.
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 // ... } }
Quando utilizzi questa modalità, assicurati che:
- I valori dei metadati delle dimensioni minima e massima sono definiti correttamente in base alle dimensioni dei contenuti.
- I contenuti sono sufficientemente flessibili all'interno dell'intervallo di dimensioni previsto.
In generale, dovresti utilizzare questa modalità quando:
a) l'elemento AppWidget
ha una dimensione fissa oppure
b) non cambia i contenuti quando viene ridimensionato.
SizeMode.Responsive
Questa modalità è equivalente alla fornitura di layout adattabili, che consente
a GlanceAppWidget
di definire un insieme di layout adattabili limitati da dimensioni
specifiche. Per ogni dimensione definita, i contenuti vengono creati e mappati alla dimensione
specifica quando l'elemento AppWidget
viene creato o aggiornato. Il sistema seleziona quindi quello più adatto in base alla dimensione disponibile.
Ad esempio, nel nostro AppWidget
di destinazione, puoi definire tre dimensioni e i relativi
contenuti:
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") } } } }
Nell'esempio precedente, il metodo provideContent
viene chiamato tre volte e
mappato alla dimensione definita.
- Nella prima chiamata, la dimensione restituisce
100x100
. I contenuti non includono il pulsante aggiuntivo, né i testi superiore e inferiore. - Nella seconda chiamata, la dimensione restituisce
250x100
. I contenuti includono il pulsante aggiuntivo, ma non i testi superiore e inferiore. - Nella terza chiamata, la dimensione restituisce
250x250
. I contenuti includono il pulsante aggiuntivo ed entrambi i testi.
SizeMode.Responsive
è una combinazione delle altre due modalità e consente di definire i contenuti adattabili entro limiti predefiniti. In generale, questa modalità
offre prestazioni migliori e consente transizioni più fluide quando l'elemento AppWidget
viene ridimensionato.
La tabella seguente mostra il valore della dimensione, in base alla dimensione disponibile (SizeMode
e AppWidget
):
Dimensioni disponibili | 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 |
* I valori esatti sono solo a scopo dimostrativo. |
SizeMode.Exact
SizeMode.Exact
equivale a fornire layout esatti, che
richiede i contenuti GlanceAppWidget
ogni volta che la dimensione AppWidget
disponibile
cambia (ad esempio, quando l'utente ridimensiona AppWidget
nella schermata Home).
Ad esempio, nel widget di destinazione, è possibile aggiungere un pulsante in più se la larghezza disponibile è superiore a un determinato valore.
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") } } } } }
Questa modalità offre più flessibilità rispetto alle altre, ma comporta alcune avvertenze:
AppWidget
deve essere ricreato completamente ogni volta che le dimensioni cambiano. Questo può portare a problemi di prestazioni e salti nell'interfaccia utente quando i contenuti sono complessi.- Le dimensioni disponibili potrebbero variare a seconda dell'implementazione di Avvio app. Ad esempio, se Avvio app non fornisce l'elenco delle dimensioni, viene utilizzata la dimensione minima possibile.
- Nei dispositivi precedenti ad Android 12, la logica di calcolo delle dimensioni potrebbe non funzionare in tutte le situazioni.
In generale, dovresti utilizzare questa modalità se non è possibile usare SizeMode.Responsive
(ovvero, non è possibile usare un piccolo insieme di layout adattabili).
Accedi alle risorse
Utilizza LocalContext.current
per accedere a qualsiasi risorsa Android, come mostrato nell'esempio seguente:
LocalContext.current.getString(R.string.glance_title)
Ti consigliamo di fornire direttamente gli ID risorsa per ridurre le dimensioni dell'oggetto RemoteViews
finale e per abilitare risorse dinamiche, ad esempio i colori dinamici.
Gli elementi componibili e i metodi accettano risorse utilizzando un "provider", come
ImageProvider
, o un metodo di sovraccarico come
GlanceModifier.background(R.color.blue)
. Ecco alcuni esempi:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Testo dell'handle
Glance 1.1.0 include un'API per impostare gli stili di testo. Imposta gli stili di testo utilizzando gli attributi
fontSize
, fontWeight
o fontFamily
della classe TextStyle.
fontFamily
supporta tutti i caratteri di sistema, come mostrato nell'esempio seguente, ma
i caratteri personalizzati nelle app non sono supportati:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Aggiungi pulsanti composti
I pulsanti composti sono stati introdotti in Android 12. Riepilogo supporta la compatibilità con le versioni precedenti per i seguenti tipi di pulsanti composti:
Questi pulsanti composti mostrano ciascuno una visualizzazione cliccabile che rappresenta lo stato "selezionato".
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" )
Quando lo stato cambia, viene attivato il lambda fornito. Puoi archiviare lo stato di controllo, come mostrato nell'esempio seguente:
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) } ) } }
Puoi anche fornire l'attributo colors
a CheckBox
, Switch
e
RadioButton
per personalizzare i relativi colori:
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) ), )
Componenti aggiuntivi
Il Glance 1.1.0 include il rilascio di componenti aggiuntivi, come descritto nella tabella seguente:
Nome | Immagine | Link di riferimento | Note aggiuntive |
---|---|---|---|
Pulsante pieno |
![]() |
Componente | |
Pulsanti con contorno |
![]() |
Componente | |
Pulsanti icona |
![]() |
Componente | Principale / Secondario / Solo icone |
Barra del titolo |
![]() |
Componente | |
Impalcatura | Scaffold e Barra del titolo si trovano nella stessa demo. |
Per ulteriori informazioni sulle specifiche del design, vedi i design dei componenti in questo design kit su Figma.