Creare un widget dell'app con Riepilogo

Le sezioni seguenti descrivono come creare un widget dell'app di base con Glance.

Dichiarare AppWidget nel file manifest

Dopo aver completato i passaggi di configurazione, dichiara AppWidget e i relativi metadati nella tua app.

  1. Estendi il AppWidget ricevitore da GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. Registra il provider del widget dell'app nel file AndroidManifest.xml e 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>
    

Aggiungere i metadati AppWidgetProviderInfo

Poi, segui la guida Creare 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>

Dichiarare il file 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 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 dei 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, la dimensione predefinita di un widget è la dimensione che il widget assume quando viene aggiunto per la prima volta alla schermata Home.

La tabella seguente descrive gli attributi <appwidget-provider> relativi al dimensionamento dei widget:

Attributi e descrizione
targetCellWidth e targetCellHeight (Android 12), minWidth e minHeight
  • A partire da Android 12, gli attributi targetCellWidth e targetCellHeight specificano la dimensione predefinita del widget in termini di celle della griglia. Questi attributi vengono ignorati in Android 11 e versioni precedenti e possono essere ignorati se la schermata Home non supporta un layout basato su griglia.
  • Gli attributi minWidth e minHeight specificano la dimensione predefinita del widget in dp. Se i valori per la larghezza o l'altezza minima di un widget non corrispondono le dimensioni delle celle, i valori vengono arrotondati per eccesso alla dimensione della cella più vicina.
Ti consigliamo di specificare entrambi i set di attributi—targetCellWidth e targetCellHeight, e 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 la dimensione minima assoluta del widget. Questi valori specificano la dimensione al di sotto della quale il widget è illeggibile o inutilizzabile. L'utilizzo di questi attributi consente all'utente di ridimensionare il widget a una dimensione inferiore a quella predefinita. L'attributo minResizeWidth viene ignorato se è maggiore di minWidth o se il ridimensionamento orizzontale non è attivato. Vedi resizeMode. Allo stesso modo, l' attributo minResizeHeightviene ignorato se è maggiore di minHeight o se il ridimensionamento verticale non è attivato.
maxResizeWidth e maxResizeHeight Specifica la dimensione massima consigliata 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 è attivato. Vedi resizeMode. Allo stesso modo, l'attributo maxResizeHeight viene ignorato se è inferiore a minHeight o se il ridimensionamento verticale non è attivato. Introdotto 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 le maniglie di ridimensionamento, quindi trascinano le maniglie orizzontali o verticali per modificarne le dimensioni nella griglia di layout. I valori dell'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 le seguenti specifiche:

  • Una cella della griglia ha una larghezza di 30 dp e un'altezza di 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 dimensione predefinita del widget.

Per impostazione predefinita, le dimensioni 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 la dimensione predefinita del widget.

Larghezza predefinita = Math.ceil(80 / 30) = 3

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 fino a schermo intero.

Attributi aggiuntivi dei widget

La tabella seguente descrive gli attributi <appwidget-provider> relativi a qualità diverse dal dimensionamento dei widget.

Attributi e descrizione
updatePeriodMillis Definisce la frequenza con cui il framework dei widget richiede un aggiornamento da GlanceAppWidgetReceiver chiamando il metodo di callback onUpdate(). Ti consigliamo di eseguire gli aggiornamenti il meno frequentemente 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 Punta alla risorsa di layout che definisce il layout di caricamento del widget prima del rendering delle composizioni dell'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 Implementare l'attività di configurazione.
description Specifica la descrizione da visualizzare per il widget nel selettore di widget. Introdotto in Android 12.
previewLayout (Android 12) e previewImage (Android 11 e versioni precedenti)
  • A partire da Android 12, l' previewLayout attributo specifica un'anteprima scalabile, che fornisci come set di layout XML impostato sulla dimensione predefinita del widget. Idealmente, questo punta a un mapping XML statico che corrisponde al layout di progettazione.
  • In Android 11 o versioni precedenti, l'attributo previewImage specifica uno screenshot dell'immagine statica del widget, che viene visualizzato nel selettore di widget.
Ti consigliamo di specificare entrambi gli attributi in modo che la tua app possa eseguire il fallback senza problemi sulle piattaforme precedenti. Per le piattaforme più recenti (Android 15 e versioni successive), puoi definire le anteprime generate in tempo reale in Kotlin utilizzando `GlanceAppWidget.providePreview`. Consulta la guida Anteprime generate.
autoAdvanceViewId Specifica l'ID della visualizzazione della sottovista 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 sia reconfigurable.

Definire GlanceAppWidget

  1. Crea una nuova classe che estenda da GlanceAppWidget e sostituisca il metodo provideGlance. Questo è il metodo in cui puoi caricare i dati necessari per 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")
            }
        }
    }

  2. Crea un'istanza in glanceAppWidget su GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

A questo punto, hai configurato un AppWidget utilizzando Glance.

Utilizzare la classe GlanceAppWidgetReceiver per gestire le trasmissioni dei widget

La classe GlanceAppWidgetReceiver coordina le trasmissioni dei widget e gli aggiornamenti dello stato della piattaforma estendendo la classe 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.

Dichiarare un widget nel file manifest

Dichiara la sottoclasse 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 a alla risorsa dei metadati XML AppWidgetProviderInfo (@xml/my_app_widget_info).

Implementare la classe GlanceAppWidgetReceiver

In Glance, estendi GlanceAppWidgetReceiver anziché AppWidgetProvider direttamente. Implementalo collegando il ricevitore all'istanza GlanceAppWidget. I callback principali disponibili in GlanceAppWidgetReceiver funzionano nel seguente modo:

  • onUpdate(): sostituito automaticamente da Glance per eseguire gli aggiornamenti della composizione. Se sostituisci manualmente onUpdate, devi chiamare super.onUpdate per consentire a Glance di avviare correttamente i thread di composizione.
  • onAppWidgetOptionsChanged(): chiamato quando il widget viene posizionato o ridimensionato per la prima volta. Glance legge gli elementi del bundle di opzioni in background, in modo che il layout si adatti perfettamente in base alle dimensioni di runtime.
  • onDeleted(Context, IntArray): richiamato ogni volta che l'utente elimina un'istanza specifica del widget.
  • onEnabled(Context): attivato quando la prima istanza del widget viene creata correttamente. Ideale per eseguire migrazioni globali.
  • onDisabled(Context): 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 la logica del ricevitore personalizzato che scrivi chiami super.onReceive(context, intent) e non deve mai chiamare goAsync , perché Glance instrada 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:

Creare UI

Il seguente snippet mostra come creare l'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>()
                )
            }
        }
    }
}

Il codice di esempio precedente esegue le seguenti operazioni:

  • Nella Column di primo livello, gli elementi vengono posizionati verticalmente uno dopo l'altro.
  • Il Column espande le dimensioni in modo che corrispondano allo spazio disponibile (tramite GlanceModifier e allinea i contenuti alla parte superiore (verticalAlignment) e li centra orizzontalmente (horizontalAlignment).
  • I contenuti di Column vengono definiti utilizzando l'espressione lambda. l'ordine è importante.
    • Il primo elemento in Column è un componente Text con un padding di 12.dp.
    • Il secondo elemento è una Row, in cui gli elementi vengono posizionati orizzontalmente uno dopo l'altro, con due Buttons centrati orizzontalmente (horizontalAlignment). La visualizzazione finale dipende dallo spazio disponibile. L'immagine seguente è un esempio di come potrebbe apparire:
destination_widget
Figura 1. Un esempio di UI.

Puoi modificare i valori di allineamento o applicare valori di modificatore diversi (ad esempio il padding) per modificare il posizionamento e le dimensioni dei componenti. Consulta la documentazione di riferimento per un elenco completo di componenti, parametri e modificatori disponibili per ogni classe.

Implementare gli angoli arrotondati

Android 12 introduce parametri di sistema per personalizzare dinamicamente i raggi degli angoli dei widget dell'app:

  • system_app_widget_background_radius: specifica il raggio dell'angolo del contenitore dello sfondo del widget (non 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 dinamicamente le proprietà di dimensionamento del raggio dell'angolo nella composizione utilizzando GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).

Per la compatibilità con le versioni precedenti sui dispositivi che eseguono Android 11 (livello API 30) o versioni precedenti, implementa attributi personalizzati e 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>