Le sezioni seguenti descrivono come creare un widget di base per l'app con Glance.
Dichiara AppWidget nel manifest
Dopo aver completato i passaggi di configurazione, dichiara AppWidget e i relativi metadati nella tua app.
Estendi il ricevitore
AppWidgetdaGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
Registra il provider del widget dell'app nel file
AndroidManifest.xmle nel file di metadati associato:<receiver android:name=".glance.MyReceiver" android:exported="true"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_app_widget_info" /> </receiver>
Aggiungi i metadati AppWidgetProviderInfo
Poi, segui la guida Crea un widget per creare e definire le informazioni del widget dell'app nel file @xml/my_app_widget_info.
L'unica differenza per Glance è che non esiste un file XML initialLayout, ma devi definirne uno. Puoi utilizzare il layout di caricamento predefinito fornito nella libreria:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
Dichiara l'XML AppWidgetProviderInfo
L'oggetto AppWidgetProviderInfo definisce le qualità essenziali del widget. Definisci AppWidgetProviderInfo nel file di risorse dei metadati XML (res/xml/my_app_widget_info.xml) all'interno di un elemento <appwidget-provider>:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Attributi di dimensionamento dei widget
La schermata Home predefinita posiziona i widget nella sua finestra in base a una griglia di celle con altezza e larghezza definite. La maggior parte delle schermate Home consente ai widget di assumere solo dimensioni che sono multipli interi delle celle della griglia, ad esempio due celle in orizzontale per tre celle in verticale.
Gli attributi di dimensionamento del widget ti consentono di specificare una dimensione predefinita per il widget e di fornire limiti inferiori e superiori per le dimensioni del widget. In questo contesto, le dimensioni predefinite di un widget sono quelle che assume quando viene aggiunto per la prima volta alla schermata Home.
La tabella seguente descrive gli attributi <appwidget-provider> relativi alle dimensioni del widget:
| Attributi e descrizione | |
|---|---|
targetCellWidth e
targetCellHeight (Android 12),
minWidth e minHeight |
targetCellWidth e targetCellHeight, nonché minWidth e minHeight, in modo che la tua app possa utilizzare minWidth e minHeight se il dispositivo dell'utente non supporta targetCellWidth e targetCellHeight. Se supportati, gli attributi targetCellWidth e targetCellHeight hanno la precedenza sugli attributi minWidth e minHeight.
|
minResizeWidth e
minResizeHeight |
Specifica le dimensioni minime assolute del widget. Questi valori specificano le dimensioni al di sotto delle quali il widget è illeggibile o altrimenti inutilizzabile. L'utilizzo di questi attributi consente all'utente di ridimensionare il widget in modo che sia più piccolo delle dimensioni predefinite. L'attributo minResizeWidth viene ignorato se è maggiore di minWidth o se il ridimensionamento orizzontale non è abilitato. Consulta resizeMode. Analogamente, l'attributo minResizeHeight viene ignorato se è maggiore di minHeight o se il ridimensionamento verticale non è abilitato. |
maxResizeWidth e
maxResizeHeight |
Specifica le dimensioni massime consigliate del widget. Se i valori non sono un multiplo delle dimensioni delle celle della griglia, vengono arrotondati per eccesso alla dimensione della cella più vicina. L'attributo maxResizeWidth viene ignorato se è inferiore a minWidth o se il ridimensionamento orizzontale non è abilitato. Vedi resizeMode. Analogamente, l'attributo maxResizeHeight viene ignorato se è inferiore a minHeight o se il ridimensionamento verticale non è abilitato.
Introdotta in Android 12. |
resizeMode |
Specifica le regole in base alle quali è possibile ridimensionare un widget. Puoi utilizzare questo attributo per rendere i widget della schermata Home ridimensionabili in orizzontale, in verticale o su entrambi gli assi. Gli utenti toccano e tengono premuto un widget per visualizzare i relativi handle di ridimensionamento, quindi trascinano gli handle orizzontali o verticali per modificarne le dimensioni sulla griglia di layout. I valori per l'attributo resizeMode includono horizontal, vertical e none. Per dichiarare un widget come ridimensionabile in orizzontale e in verticale, utilizza horizontal|vertical. |
Esempio
Per illustrare in che modo gli attributi della tabella precedente influiscono sul dimensionamento dei widget, supponiamo che le specifiche siano le seguenti:
- Una cella della griglia è larga 30 dp e alta 50 dp.
- Viene fornita la seguente specifica dell'attributo:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
A partire da Android 12:
Utilizza gli attributi targetCellWidth e targetCellHeight come dimensioni predefinite del widget.
Le dimensioni predefinite del widget sono 2x2. Il widget può essere ridimensionato fino a 2x1 o fino a 4x3.
Android 11 e versioni precedenti:
Utilizza gli attributi minWidth e minHeight per calcolare le dimensioni predefinite del widget.
La larghezza predefinita = Math.ceil(80 / 30) = 3
L'altezza predefinita = Math.ceil(80 / 50) = 2
Per impostazione predefinita, le dimensioni del widget sono 3x2. Il widget può essere ridimensionato fino a 2x1 o a schermo intero.
Attributi aggiuntivi del widget
La tabella seguente descrive gli attributi <appwidget-provider> relativi
a qualità diverse dalle dimensioni del widget.
| Attributi e descrizione | |
|---|---|
updatePeriodMillis |
Definisce la frequenza con cui il framework del widget richiede un aggiornamento da GlanceAppWidgetReceiver chiamando il metodo di callback onUpdate(). Ti consigliamo di aggiornare il meno possibile, non più di una volta all'ora, per risparmiare la batteria.
Per maggiori dettagli, consulta la sezione Quando aggiornare i widget in Gestione dello stato di Glance. |
initialLayout |
Indica la risorsa di layout che definisce il layout di caricamento del widget prima del rendering delle composizioni della UI di Glance. Puoi utilizzare il layout di caricamento predefinito fornito nella libreria: @layout/glance_default_loading_layout. |
configure |
Definisce l'attività di configurazione che viene avviata quando l'utente aggiunge il widget. Consulta la guida Consenti agli utenti di configurare i widget delle app. |
description |
Specifica la descrizione che il selettore dei widget deve mostrare per il tuo widget. Introdotta in Android 12. |
previewLayout (Android 12)
e previewImage (Android 11 e versioni precedenti) |
|
autoAdvanceViewId |
Specifica l'ID visualizzazione della visualizzazione secondaria del widget che viene avanzata automaticamente dall'host del widget. |
widgetCategory |
Dichiara se il widget può essere visualizzato nella schermata Home (home_screen), nella schermata di blocco (keyguard) o in entrambe. Per Android 5.0 e versioni successive, è valido solo home_screen. |
widgetFeatures |
Dichiara le funzionalità supportate dal widget. Ad esempio, se la configurazione del widget è facoltativa, specifica sia configuration_optional che reconfigurable. |
Definisci GlanceAppWidget
Crea una nuova classe che estende
GlanceAppWidgeted esegue l'override del metodoprovideGlance. Questo è il metodo in cui puoi caricare i dati necessari per eseguire il rendering del widget:class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // In this method, load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here Text("Hello World") } } }
Crea un'istanza nel
glanceAppWidgetsul tuoGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
Ora hai configurato un AppWidget utilizzando Glance.
Gestisci la memoria delle immagini dei widget in base alle dimensioni
Quando il widget mostra bitmap o URI di immagini content://, mantieni la memoria totale delle immagini RemoteViews serializzate entro il limite di sistema (6 * width *
height byte, applicato con un IllegalArgumentException su Android 17 e versioni successive) procedendo nel seguente modo:
- Decodifica le bitmap condivise prima di
provideContent: se il widget utilizzaSizeMode.ResponsiveoSizeMode.Exact, Glance esegue la funzione lambdaprovideContentuna volta per ogni bucketDpSize. La decodifica di unBitmapall'interno diprovideContentalloca un'istanzaBitmapseparata per ogni bucket, moltiplicando la memoria utilizzata inRemoteViews.mBitmapCache. Decodifica le bitmap con downsampling (Bitmap.Config.RGB_565per le immagini opache) una volta all'interno diprovideGlanceprima diprovideContentin modo che tutti i bucket condividano un unico riferimentoBitmap. Utilizza l'importazione
ImageProvidercorrispondente: Jetpack Glance fornisce due overloadImageProviderdistinti in due pacchetti:- Utilizza
import androidx.glance.ImageProviderquando passi una risorsaImageProvider(bitmap: Bitmap)in memoria o una risorsa drawableImageProvider(resId: Int). - Utilizza
import androidx.glance.appwidget.ImageProviderquando passi un URIImageProvider(uri: Uri). Quando fornisci file memorizzati nella cache tramite un URIFileProvider, concediIntent.FLAG_GRANT_READ_URI_PERMISSIONa ogni pacchetto di avvioIntent.CATEGORY_HOMEinstallato in modo che l'host possa eseguire il rendering dell'immagine.
- Utilizza
Utilizza la classe GlanceAppWidgetReceiver per gestire le trasmissioni dei widget
Il widget delle coordinate GlanceAppWidgetReceiver trasmette e aggiorna lo stato della piattaforma estendendo l'elemento AppWidgetProvider sottostante. Riceve gli eventi della piattaforma quando il widget viene aggiornato, eliminato, attivato o disattivato, traducendoli in richieste del ciclo di vita di Compose.
Dichiara un widget nel manifest
Dichiara la sottoclasse della classe GlanceAppWidgetReceiver come broadcast receiver nel file AndroidManifest.xml:
<receiver android:name="MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info" />
</receiver>
L'elemento <receiver> richiede l'attributo android:name, che specifica la classe del ricevitore. Il ricevitore deve accettare l'azione di trasmissione ACTION_APPWIDGET_UPDATE all'interno di <intent-filter>.
L'elemento <meta-data> deve identificare il proprio nome come android.appwidget.provider e l'attributo android:resource deve puntare alla risorsa di metadati XML AppWidgetProviderInfo (@xml/my_app_widget_info).
Implementa la classe GlanceAppWidgetReceiver
In Glance, estendi GlanceAppWidgetReceiver anziché
AppWidgetProvider direttamente. Implementalo collegando il ricevitore alla tua istanza GlanceAppWidget. I callback principali disponibili in GlanceAppWidgetReceiver funzionano come segue:
onUpdate(): viene eseguito automaticamente l'override da parte di Glance per eseguire gli aggiornamenti della composizione. Se esegui l'override manuale dionUpdate, devi chiamaresuper.onUpdateper consentire a Glance di avviare correttamente i thread di composizione.onAppWidgetOptionsChanged(): viene chiamato quando il widget viene posizionato o ridimensionato per la prima volta. Glance legge le opzioni e raggruppa gli elementi in background in modo che il layout si adatti perfettamente in base alle dimensioni di runtime.onDeleted(Context, IntArray): viene richiamato ogni volta che un'istanza di widget specifica viene eliminata dall'utente.onEnabled(Context): attivato quando la prima istanza del widget viene creata correttamente. Eccellente per l'esecuzione di migrazioni globali.onDisabled(Context): viene chiamato quando viene rimossa l'ultima istanza attiva del provider.onReceive(Context, Intent): intercetta ogni trasmissione della piattaforma prima di metodi di callback specifici. Devi assicurarti che qualsiasi logica del ricevitore personalizzato che scrivi chiamisuper.onReceive(context, intent)e non devi mai chiamaregoAsyncautonomamente, poiché Glance indirizza automaticamente il lavoro in modo asincrono.
Ricevere intent di trasmissione dei widget
In background, GlanceAppWidgetReceiver filtra e gestisce i seguenti intent di trasmissione dei widget della piattaforma di base:
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
Crea UI
Il seguente snippet mostra come creare la UI:
/* Import Glance Composables In the event there is a name clash with the Compose classes of the same name, you may rename the imports per https://kotlinlang.org/docs/packages.html#imports using the `as` keyword. import androidx.glance.Button import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.text.Text */ class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // Load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here MyContent() } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MyActivity>() ) Button( text = "Work", onClick = actionStartActivity<MyActivity>() ) } } } }
L'esempio di codice precedente esegue queste operazioni:
- Nel
Columndi primo livello, gli elementi vengono posizionati verticalmente uno dopo l'altro. Columnespande le sue dimensioni per adattarsi allo spazio disponibile (tramiteGlanceModifiere allinea i suoi contenuti in alto (verticalAlignment) e li centra orizzontalmente (horizontalAlignment).- I contenuti di
Columnvengono definiti utilizzando l'espressione lambda. l'ordine è importante.- Il primo elemento in
Columnè un componenteTextcon12.dpdi padding. - Il secondo elemento è un
Row, in cui gli elementi sono posizionati orizzontalmente uno dopo l'altro, con dueButtonscentrati orizzontalmente (horizontalAlignment). La visualizzazione finale dipende dallo spazio disponibile. L'immagine seguente è un esempio di come potrebbe apparire:
- Il primo elemento in
Puoi modificare i valori di allineamento o applicare valori di modificatore diversi (ad esempio il padding) per modificare la posizione e le dimensioni dei componenti. Consulta la documentazione di riferimento per un elenco completo di componenti, parametri e modificatori disponibili per ogni classe.
Implementa gli angoli arrotondati
Android 12 introduce parametri di sistema per personalizzare dinamicamente i raggi degli angoli dei widget delle app:
system_app_widget_background_radius: specifica il raggio dell'angolo del container di sfondo del widget (mai superiore a 28 dp).- Raggio interno:per evitare il ritaglio dei contenuti, calcola un raggio proporzionale per i contenuti interni in base al contorno dello sfondo del sistema:
systemRadiusValue - widgetPadding
In Glance, puoi applicare le proprietà di dimensionamento del raggio degli angoli in modo dinamico nella composizione utilizzando GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).
Per la compatibilità con le versioni precedenti sui dispositivi con Android 11 (livello API 30) o versioni precedenti, implementa attributi personalizzati e i fallback delle risorse dei temi personalizzati:
/values/attrs.xml<resources> <attr name="backgroundRadius" format="dimension" /> </resources>/values/styles.xml<resources> <style name="MyWidgetTheme"> <item name="backgroundRadius">@dimen/my_background_radius_dimen</item> </style> </resources>/values-31/styles.xml<resources> <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight"> <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item> </style> </resources>/drawable/my_widget_background.xml<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="?attr/backgroundRadius" /> </shape>