App-Widget mit Glance erstellen

Manifest, Metadaten

In den folgenden Abschnitten wird beschrieben, wie Sie mit Glance ein einfaches App-Widget erstellen.

AppWidget im Manifest deklarieren

Nachdem Sie die Einrichtungsschritte abgeschlossen haben, deklarieren Sie das AppWidget und seine Metadaten in Ihrer App.

  1. Erweitere den AppWidget-Empfänger von GlanceAppWidgetReceiver:

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

  2. Registrieren Sie den Anbieter des App-Widgets in Ihrer AndroidManifest.xml-Datei und der zugehörigen Metadatendatei:

        <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>
    

AppWidgetProviderInfo-Metadaten hinzufügen

Folgen Sie als Nächstes der Anleitung unter Widget erstellen, um die App-Widget-Informationen in der Datei @xml/my_app_widget_info zu erstellen und zu definieren.

Der einzige Unterschied für Glances besteht darin, dass es kein initialLayout-XML gibt, Sie aber eines definieren müssen. Sie können das vordefinierte Lade-Layout aus der Bibliothek verwenden:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

AppWidgetProviderInfo-XML deklarieren

Das AppWidgetProviderInfo-Objekt definiert die wesentlichen Eigenschaften Ihres Widgets. Definieren Sie die AppWidgetProviderInfo in Ihrer XML-Metadatenressourcendatei (res/xml/my_app_widget_info.xml) innerhalb eines <appwidget-provider>-Elements:

<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>

Attribute für die Widget-Größe

Auf dem Standard-Startbildschirm werden Widgets in einem Fenster auf Grundlage eines Rasters von Zellen mit definierter Höhe und Breite positioniert. Auf den meisten Startbildschirmen können Widgets nur Größen annehmen, die ganzzahlige Vielfache der Rasterzellen sind, z. B. zwei Zellen horizontal mal drei Zellen vertikal.

Mit den Attributen für die Widget-Größe können Sie eine Standardgröße für Ihr Widget festlegen und Unter- und Obergrenzen für die Größe des Widgets angeben. In diesem Zusammenhang ist die Standardgröße eines Widgets die Größe, die das Widget annimmt, wenn es zum ersten Mal dem Startbildschirm hinzugefügt wird.

In der folgenden Tabelle werden die <appwidget-provider>-Attribute für die Widget-Größe beschrieben:

Attribute und Beschreibung
targetCellWidth und targetCellHeight (Android 12), minWidth und minHeight
  • Ab Android 12 geben die Attribute targetCellWidth und targetCellHeight die Standardgröße des Widgets in Bezug auf Rasterzellen an. Diese Attribute werden in Android 11 und niedriger ignoriert und können ignoriert werden, wenn der Startbildschirm kein rasterbasiertes Layout unterstützt.
  • Die Attribute minWidth und minHeight geben die Standardgröße des Widgets in dp an. Wenn die Werte für die Mindestbreite oder ‑höhe eines Widgets nicht mit den Abmessungen der Zellen übereinstimmen, werden die Werte auf die nächste Zellengröße aufgerundet.
Wir empfehlen, beide Attributgruppen anzugeben: targetCellWidth und targetCellHeight sowie minWidth und minHeight. So kann Ihre App auf minWidth und minHeight zurückgreifen, wenn das Gerät des Nutzers targetCellWidth und targetCellHeight nicht unterstützt. Falls unterstützt, haben die Attribute targetCellWidth und targetCellHeight Vorrang vor den Attributen minWidth und minHeight.
minResizeWidth und minResizeHeight Geben Sie die absolute Mindestgröße des Widgets an. Diese Werte geben die Größe an, unter der das Widget nicht mehr lesbar oder anderweitig nicht mehr nutzbar ist. Mit diesen Attributen kann der Nutzer die Größe des Widgets auf eine Größe anpassen, die kleiner als die Standardgröße des Widgets ist. Das Attribut minResizeWidth wird ignoriert, wenn es größer als minWidth ist oder wenn die horizontale Größenanpassung nicht aktiviert ist. Weitere Informationen finden Sie unter resizeMode. Ebenso wird das Attribut minResizeHeight ignoriert, wenn es größer als minHeight ist oder wenn die vertikale Größenanpassung nicht aktiviert ist.
maxResizeWidth und maxResizeHeight Geben Sie die empfohlene maximale Größe des Widgets an. Wenn die Werte kein Vielfaches der Rasterzellendimensionen sind, werden sie auf die nächste Zellengröße aufgerundet. Das Attribut maxResizeWidth wird ignoriert, wenn es kleiner als minWidth ist oder wenn die horizontale Größenanpassung nicht aktiviert ist. Siehe resizeMode. Ebenso wird das Attribut maxResizeHeight ignoriert, wenn es kleiner als minHeight ist oder die vertikale Größenanpassung nicht aktiviert ist. Eingeführt in Android 12
resizeMode Gibt die Regeln an, nach denen die Größe eines Widgets geändert werden kann. Mit diesem Attribut können Sie festlegen, dass Startbildschirm-Widgets horizontal, vertikal oder auf beiden Achsen in der Größe angepasst werden können. Nutzer halten ein Widget gedrückt, um die Ziehpunkte zum Anpassen der Größe aufzurufen. Anschließend ziehen sie die horizontalen oder vertikalen Ziehpunkte, um die Größe des Widgets im Layoutraster zu ändern. Mögliche Werte für das Attribut resizeMode sind horizontal, vertical und none. Wenn Sie ein Widget als horizontal und vertikal anpassbar deklarieren möchten, verwenden Sie horizontal|vertical.

Beispiel

Zur Veranschaulichung der Auswirkungen der Attribute in der vorherigen Tabelle auf die Widget-Größe nehmen wir die folgenden Spezifikationen an:

  • Eine Rasterzelle ist 30 dp breit und 50 dp hoch.
  • Die folgende Attributspezifikation ist verfügbar:
<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" />

Ab Android 12:

Verwenden Sie die Attribute targetCellWidth und targetCellHeight als Standardgröße des Widgets.

Die Standardgröße des Widgets ist 2 × 2. Die Größe des Widgets kann auf 2 × 1 oder bis zu 4 × 3 geändert werden.

Android 11 und niedriger:

Mit den Attributen minWidth und minHeight können Sie die Standardgröße des Widgets berechnen.

Die Standardbreite ist Math.ceil(80 / 30) = 3.

Die Standardhöhe ist Math.ceil(80 / 50) = 2.

Die Standardgröße des Widgets ist 3 × 2. Die Größe des Widgets kann auf 2 × 1 oder auf Vollbild geändert werden.

Zusätzliche Widget-Attribute

In der folgenden Tabelle werden die <appwidget-provider>-Attribute beschrieben, die sich auf andere Aspekte als die Widget-Größe beziehen.

Attribute und Beschreibung
updatePeriodMillis Legt fest, wie oft das Widget-Framework ein Update von GlanceAppWidgetReceiver anfordert, indem die Callback-Methode onUpdate() aufgerufen wird. Wir empfehlen, die Daten so selten wie möglich zu aktualisieren, um den Akku zu schonen. Aktualisieren Sie die Daten nicht öfter als einmal pro Stunde. Weitere Informationen finden Sie im Abschnitt Glance-Statusverwaltung unter „Wann Widgets aktualisiert werden“.
initialLayout Verweist auf die Layoutressource, die das Ladelayout des Widgets definiert, bevor die Glance-UI-Kompositionen gerendert werden. Sie können das in der Bibliothek bereitgestellte vordefinierte Lade-Layout verwenden: @layout/glance_default_loading_layout.
configure Definiert die Konfigurationsaktivität, die gestartet wird, wenn der Nutzer das Widget hinzufügt. Weitere Informationen finden Sie im Abschnitt Widget-Konfigurations-Activity implementieren auf dieser Seite.
description Gibt die Beschreibung an, die für Ihr Widget in der Widget-Auswahl angezeigt werden soll. Eingeführt in Android 12
previewLayout (Android 12) und previewImage (Android 11 und niedriger)
  • Ab Android 12 gibt das Attribut previewLayout eine skalierbare Vorschau an, die Sie als XML-Layout in der Standardgröße des Widgets bereitstellen. Im Idealfall verweist dies auf eine statische XML-Zuordnung, die Ihrem Designlayout entspricht.
  • In Android 11 oder niedriger gibt das Attribut previewImage einen statischen Screenshot des Widgets an, der in der Widget-Auswahl angezeigt wird.
Wir empfehlen, beide anzugeben, damit Ihre App auf älteren Plattformen ordnungsgemäß ausgeführt wird. Auf neueren Plattformen (Android 15+) können Sie live generierte Vorschauen in Kotlin mit `GlanceAppWidget.providePreview` definieren. Weitere Informationen finden Sie im Leitfaden zu generierten Vorschauen.
autoAdvanceViewId Gibt die Ansichts-ID der untergeordneten Ansicht des Widgets an, die vom Host des Widgets automatisch weitergeschaltet wird.
widgetCategory Gibt an, ob Ihr Widget auf dem Startbildschirm (home_screen), dem Sperrbildschirm (keyguard) oder beiden angezeigt werden kann. Bei Android 5.0 und höher ist nur home_screen gültig.
widgetFeatures Deklariert die vom Widget unterstützten Funktionen. Wenn die Konfiguration Ihres Widgets beispielsweise optional ist, geben Sie sowohl configuration_optional als auch reconfigurable an.

GlanceAppWidget definieren

  1. Erstellen Sie eine neue Klasse, die von GlanceAppWidget abgeleitet wird und die Methode provideGlance überschreibt. Mit dieser Methode können Sie Daten laden, die zum Rendern Ihres Widgets erforderlich sind:

    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. Instanziieren Sie sie in der glanceAppWidget auf Ihrem GlanceAppWidgetReceiver:

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

Sie haben jetzt eine AppWidget mit Glance konfiguriert.

AppWidgetProvider-Klasse zum Verarbeiten von Widget-Broadcasts verwenden

Das GlanceAppWidgetReceiver-Koordinaten-Widget überträgt Broadcasts und Plattformstatus-Updates, indem es die zugrunde liegende AppWidgetProvider erweitert. Sie empfängt Plattformereignisse, wenn Ihr Widget aktualisiert, gelöscht, aktiviert oder deaktiviert wird, und übersetzt sie in Compose-Lebenszyklusanfragen.

Widget im Manifest deklarieren

Deklarieren Sie die Unterklasse Ihrer GlanceAppWidgetReceiver-Klasse als Übertragungsempfänger in Ihrer AndroidManifest.xml-Datei:

<receiver android:name="ExampleAppWidgetReceiver"
          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>

Für das <receiver>-Element ist das Attribut android:name erforderlich, das die Empfängerklasse angibt. Der Empfänger muss die ACTION_APPWIDGET_UPDATE-Broadcast-Aktion in der <intent-filter> akzeptieren.

Das <meta-data>-Element muss seinen Namen als android.appwidget.provider angeben und das Attribut android:resource muss auf Ihre AppWidgetProviderInfo-XML-Metadatenressource (@xml/my_app_widget_info) verweisen.

AppWidgetProvider-Klasse implementieren

In Glance erweitern Sie GlanceAppWidgetReceiver anstelle von AppWidgetProvider. Implementieren Sie sie, indem Sie Ihren Receiver mit Ihrer GlanceAppWidget-Instanz verknüpfen. Die primären Callbacks, die in GlanceAppWidgetReceiver verfügbar sind, funktionieren so:

  • onUpdate(): Wird automatisch von Glance überschrieben, um Kompositionsupdates auszuführen. Wenn Sie onUpdate manuell überschreiben, müssen Sie super.onUpdate aufrufen, damit Glance Kompositionsthreads erfolgreich starten kann.
  • onAppWidgetOptionsChanged(): Wird aufgerufen, wenn das Widget zum ersten Mal platziert oder in der Größe angepasst wird. Bei Glance-Lesevorgängen werden Elemente im Hintergrund gebündelt, sodass sich das Layout basierend auf den Laufzeitdimensionen nahtlos anpasst.
  • onDeleted(Context, IntArray): Wird aufgerufen, wenn eine bestimmte Widget-Instanz vom Nutzer gelöscht wird.
  • onEnabled(Context): Wird ausgelöst, wenn die erste Instanz Ihres Widgets erfolgreich erstellt wurde. Hervorragend für globale Migrationen geeignet.
  • onDisabled(Context): Wird aufgerufen, wenn die letzte aktive Instanz des Anbieters entfernt wird.
  • onReceive(Context, Intent): Fängt jeden Plattform-Broadcast vor bestimmten Callback-Methoden ab. Sie müssen dafür sorgen, dass in der benutzerdefinierten Empfängerlogik, die Sie schreiben, super.onReceive(context, intent) aufgerufen wird und goAsync niemals selbst aufgerufen wird, da Glance Aufgaben automatisch asynchron weiterleitet.

Broadcast-Intents für Widgets empfangen

Im Hintergrund filtert und verarbeitet GlanceAppWidgetReceiver die folgenden Broadcast-Intents für grundlegende Plattform-Widgets:

UI erstellen

Das folgende Snippet zeigt, wie die Benutzeroberfläche erstellt wird:

/* 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>()
                )
            }
        }
    }
}

Das vorhergehende Codebeispiel führt folgende Schritte aus:

  • Auf der obersten Ebene Column werden Elemente vertikal untereinander platziert.
  • Die Column wird so vergrößert, dass sie dem verfügbaren Platz entspricht (über GlanceModifier), und der Inhalt wird oben (verticalAlignment) und horizontal zentriert (horizontalAlignment) ausgerichtet.
  • Der Inhalt von Column wird mit dem Lambda definiert. Die Reihenfolge ist wichtig.
    • Das erste Element in Column ist eine Text-Komponente mit einem 12.dp-Abstand.
    • Das zweite Element ist ein Row, in dem Elemente horizontal nebeneinander platziert werden, mit zwei horizontal zentrierten Buttons (horizontalAlignment). Die endgültige Darstellung hängt vom verfügbaren Platz ab. Das folgende Bild zeigt ein Beispiel:
destination_widget
Abbildung 1. Beispiel für eine Benutzeroberfläche.

Sie können die Ausrichtungswerte ändern oder verschiedene Modifikatorwerte (z. B. Padding) anwenden, um die Position und Größe der Komponenten zu ändern. Eine vollständige Liste der Komponenten, Parameter und verfügbaren Modifizierer für jede Klasse finden Sie in der Referenzdokumentation.

Abgerundete Ecken implementieren

In Android 12 werden Systemparameter eingeführt, mit denen die Eckenradien von App-Widgets dynamisch angepasst werden können:

  • system_app_widget_background_radius: Gibt den Eckenradius des Hintergrundcontainers des Widgets an (nie größer als 28 dp).
  • Innerer Radius:Damit Inhalte nicht abgeschnitten werden, berechnen Sie einen proportionalen Radius für Ihre inneren Inhalte basierend auf der Umrisslinie des Systemhintergrunds: systemRadiusValue - widgetPadding

In Glance können Sie die Größenanpassungseigenschaften für den Eckenradius dynamisch in der Komposition mit GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius) anwenden.

Zur Abwärtskompatibilität auf Geräten mit Android 11 (API-Level 30) oder niedriger müssen Sie benutzerdefinierte Attribute und Fallbacks für benutzerdefinierte Themenressourcen implementieren:

  • /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>