Questa pagina descrive come gestire le dimensioni e fornire layout flessibili e adattabili con Riepilogo utilizzando i componenti esistenti di Riepilogo.
Utilizza Box
, Column
e Row
Glance ha tre layout composibili principali:
Box
: posiziona gli elementi uno sopra l'altro. Si traduce in unRelativeLayout
.Column
: inserisce gli elementi uno dopo l'altro nell'asse verticale. Viene visualizzato in unLinearLayout
con orientamento verticale.Row
: inserisce gli elementi uno dopo l'altro nell'asse orizzontale. Si traduce in unLinearLayout
con orientamento orizzontale.
Riepilogo supporta gli oggetti Scaffold
. Inserisci i composabili Column
, Row
e
Box
all'interno di un determinato oggetto Scaffold
.
Ciascuno di questi composabili ti consente di definire gli allineamenti verticali e orizzontali del relativo contenuto e i vincoli di larghezza, altezza, spessore o spaziatura utilizzando i modificatori. Inoltre, ogni elemento secondario può definire il proprio modificatore per modificare lo spazio e il posizionamento all'interno dell'elemento principale.
L'esempio seguente mostra come creare un elemento Row
che distribuisca uniformemente i suoi elementi secondari in orizzontale, come mostrato nella Figura 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
riempie la larghezza massima disponibile e, poiché ogni bambino ha lo stesso peso, condividono equamente lo spazio disponibile. Puoi definire diversi spessori, dimensioni, spaziature o allineamenti per adattare i layout alle tue esigenze.
Utilizzare layout scorrevoli
Un altro modo per fornire contenuti adattabili è renderli scorrevoli. Questo è possibile con il composable LazyColumn
. Questo composable ti consente di definire un insieme di elementi da visualizzare all'interno di un contenitore scorrevole nel widget dell'app.
Gli snippet seguenti mostrano diversi modi per definire gli elementi all'interno di LazyColumn
.
Puoi fornire il numero di articoli:
// 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 articoli:
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 itemId
. La specifica di itemId
contribuisce a migliorare le prestazioni e a mantenere la posizione di scorrimento tramite gli aggiornamenti dell'elenco e di appWidget
a partire da Android 12 (ad esempio, quando aggiungi o rimuovi 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 dei AppWidget
possono variare in base al dispositivo, alla scelta dell'utente o al programma di avvio, pertanto è importante fornire layout flessibili come descritto nella pagina Fornire layout dei widget flessibili. Glance semplifica questa operazione con la definizione SizeMode
e il valore LocalSize
. Le sezioni seguenti descrivono le tre modalità.
SizeMode.Single
SizeMode.Single
è la modalità predefinita. Indica che viene fornito un solo tipo di contenuti, ovvero che anche se le dimensioni disponibili di AppWidget
cambiano, le dimensioni dei contenuti non cambiano.
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 relativi alle dimensioni minime e massime sono definiti correttamente in base alle dimensioni dei contenuti.
- I contenuti sono sufficientemente flessibili nell'intervallo di dimensioni previsto.
In generale, ti consigliamo di utilizzare questa modalità quando:
a) l'elemento AppWidget
ha dimensioni fisse oppure
b) i contenuti non cambiano quando le dimensioni vengono modificate.
SizeMode.Responsive
Questa modalità è equivalente alla fornitura di layout adattabili, che consente al GlanceAppWidget
di definire un insieme di layout adattabili delimitati da dimensioni specifiche. Per ogni dimensione definita, i contenuti vengono creati e mappati alla dimensione specifica quando AppWidget
viene creato o aggiornato. Il sistema seleziona poi la dimensione più adatta in base alle dimensioni disponibili.
Ad esempio, nella nostra destinazione AppWidget
, 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 ha il valore
100x100
. I contenuti non includono il pulsante aggiuntivo né i testi in alto e in basso. - Nella seconda chiamata, la dimensione ha il valore
250x100
. I contenuti includono il pulsante extra, ma non i testi in alto e in basso. - Nella terza chiamata, la dimensione ha il valore
250x250
. I contenuti includono il pulsante extra e entrambi i testi.
SizeMode.Responsive
è una combinazione delle altre due modalità e ti consente di definire contenuti adattabili entro limiti predefiniti. In generale, questa modalità offre un rendimento migliore e consente transizioni più fluide quando le dimensioni di AppWidget
vengono modificate.
La tabella seguente mostra il valore della dimensione, a seconda delle dimensioni SizeMode
e AppWidget
disponibili:
Taglia disponibile | 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
è l'equivalente di fornire layout esatti, che richiede i contenuti GlanceAppWidget
ogni volta che le dimensioni del AppWidget
disponibili cambiano (ad esempio, quando l'utente ridimensiona il AppWidget
nella schermata Home).
Ad esempio, nel widget di destinazione è possibile aggiungere un pulsante aggiuntivo 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 maggiore flessibilità rispetto alle altre, ma presenta alcuni inconvenienti:
AppWidget
deve essere ricreato completamente ogni volta che le dimensioni cambiano. Ciò può portare a problemi di prestazioni e a salti nell'interfaccia utente quando i contenuti sono complessi.- Le dimensioni disponibili potrebbero variare a seconda dell'implementazione del programma di avvio. Ad esempio, se il programma di avvio 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, ti consigliamo di utilizzare questa modalità se non puoi utilizzare SizeMode.Responsive
(ovvero se non è possibile creare un piccolo insieme di layout adattabili).
Accedere 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 attivare le risorse dinamiche, come i colori dinamici.
I composabili e i metodi accettano le risorse utilizzando un "provider", ad esempio
ImageProvider
, o un metodo di sovraccarico come
GlanceModifier.background(R.color.blue)
. Ad esempio:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Gestire il testo
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
non supporta i caratteri personalizzati nelle app:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Aggiungere pulsanti composti
I pulsanti composti sono stati introdotti in Android 12. Glance supporta la compatibilità con le versioni precedenti per i seguenti tipi di pulsanti composti:
Questi pulsanti composti mostrano ciascuno una visualizzazione selezionabile 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 attivata la funzione lambda fornita. Puoi memorizzare lo stato del 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 personalizzarne i 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
Glance 1.1.0 include il rilascio di componenti aggiuntivi, come descritto nella tabella seguente:
Nome | Immagine | Link di riferimento | Note aggiuntive |
---|---|---|---|
Pulsante con riempimento | Componente | ||
Pulsanti con contorni | Componente | ||
Pulsanti con icone | Componente | Principale / secondaria / solo icona | |
Barra del titolo | Componente | ||
Scaffold | La struttura di base e la barra del titolo si trovano nella stessa demo. |
Per ulteriori informazioni sulle specifiche del design, consulta i design dei componenti in questo kit di design su Figma.
Per ulteriori informazioni sui layout canonici, consulta Layout dei widget canonici.