Versionsverwaltung für Ansichten

Auf Wear OS-Geräten werden Kacheln von zwei Schlüsselkomponenten mit unabhängiger Versionsverwaltung gerendert. Damit die Kacheln Ihrer Apps auf allen Geräten richtig funktionieren, ist es wichtig, diese zugrunde liegende Architektur zu verstehen.

  • Jetpack-Bibliotheken für Kacheln: Diese Bibliotheken (einschließlich Wear Tiles und Wear ProtoLayout) sind in Ihre App eingebettet und Sie als Entwickler steuern ihre Versionen. Ihre App verwendet diese Bibliotheken, um als Reaktion auf den Systemaufruf onTileRequest() ein TileBuilder.Tile-Objekt (die Datenstruktur, die Ihre Kachel darstellt) zu erstellen.
  • ProtoLayout Renderer:Diese Systemkomponente ist für das Rendern des Tile-Objekts auf dem Display und die Verarbeitung von Nutzerinteraktionen verantwortlich. Die Version des Renderers wird nicht vom App-Entwickler gesteuert und kann auf verschiedenen Geräten variieren, auch auf Geräten mit identischer Hardware.

Das Aussehen oder Verhalten einer Kachel kann je nach den Jetpack Tiles-Bibliotheksversionen Ihrer App und der ProtoLayout Renderer-Version auf dem Gerät des Nutzers variieren. Ein Gerät unterstützt beispielsweise die Drehung oder die Anzeige von Herzfrequenzdaten, ein anderes möglicherweise nicht.

In diesem Dokument wird erläutert, wie Sie dafür sorgen, dass Ihre App mit verschiedenen Versionen der Tiles-Bibliothek und des ProtoLayout-Renderers kompatibel ist, und wie Sie zu höheren Jetpack-Bibliotheksversionen migrieren.

Kompatibilität berücksichtigen

Damit eine Kachel auf einer Vielzahl von Geräten richtig funktioniert, solltest du die unterschiedliche Unterstützung von Funktionen berücksichtigen. Dazu gibt es zwei Hauptstrategien: Erkennen der Renderer-Funktionen zur Laufzeit und Bereitstellen integrierter Fallbacks.

Renderer-Funktionen erkennen

Sie können das Layout Ihrer Kachel dynamisch an die auf einem bestimmten Gerät verfügbaren Funktionen anpassen.

Renderer-Version erkennen

  • Verwenden Sie die Methode getRendererSchemaVersion() des DeviceParameters-Objekts, das an Ihre Methode onTileRequest() übergeben wird. Diese Methode gibt die Haupt- und Nebenversionsnummern des ProtoLayout-Renderers auf dem Gerät zurück.
  • Anschließend können Sie in Ihrer onTileRequest()-Implementierung eine bedingte Logik verwenden, um das Design oder Verhalten der Kachel basierend auf der erkannten Renderer-Version anzupassen.

Die Annotation @RequiresSchemaVersion

  • Die Annotation @RequiresSchemaVersion für ProtoLayout-Methoden gibt die Mindestversion des Renderer-Schemas an, die erforderlich ist, damit sich die Methode wie dokumentiert verhält (Beispiel).
    • Wenn Sie eine Methode aufrufen, für die eine höhere Renderer-Version als auf dem Gerät verfügbar ist, stürzt Ihre App zwar nicht ab, es kann aber dazu führen, dass Inhalte nicht angezeigt oder die Funktion ignoriert wird.

Beispiel für die Versionserkennung

val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion

val arcElement =
    // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500)
    // and so is supported by renderer versions 1.500 and greater
    if (
        rendererVersion.major > 1 ||
        (rendererVersion.major == 1 && rendererVersion.minor >= 500)
    ) {
        // Use DashedArcLine if the renderer supports it …
        DashedArcLine.Builder()
            .setLength(degrees(270f))
            .setThickness(8f)
            .setLinePattern(
                LayoutElementBuilders.DashedLinePattern.Builder()
                    .setGapSize(8f)
                    .setGapInterval(10f)
                    .build()
            )
            .build()
    } else {
        // … otherwise use ArcLine.
        ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build()
    }

Alternativen bereitstellen

Bei einigen Ressourcen können Sie einen Fallback direkt im Builder definieren. Das ist oft einfacher als die Prüfung der Renderer-Version und die bevorzugte Methode, wenn sie verfügbar ist.

Ein häufiger Anwendungsfall ist die Bereitstellung eines statischen Bildes als Fallback für eine Lottie-Animation. Wenn das Gerät keine Lottie-Animationen unterstützt, wird stattdessen das statische Bild gerendert.

val lottieImage =
    ResourceBuilders.ImageResource.Builder()
        .setAndroidLottieResourceByResId(
            ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie)
                .setStartTrigger(createOnVisibleTrigger())
                .build()
        )
        // Fallback if lottie is not supported
        .setAndroidResourceByResId(
            ResourceBuilders.AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.lottie_fallback)
                .build()
        )
        .build()

Mit verschiedenen Renderer-Versionen testen

Wenn Sie Ihre Kacheln mit verschiedenen Renderer-Versionen testen möchten, stellen Sie sie in verschiedenen Versionen des Wear OS-Emulators bereit. Auf physischen Geräten werden ProtoLayout Renderer-Updates über den Play Store oder Systemupdates bereitgestellt. Es ist nicht möglich, die Installation einer bestimmten Renderer-Version zu erzwingen.)

Die Funktion „Kachelvorschau“ in Android Studio verwendet einen Renderer, der in die Jetpack ProtoLayout-Bibliothek eingebettet ist, von der Ihr Code abhängt. Eine andere Möglichkeit besteht also darin, beim Testen von Kacheln von verschiedenen Jetpack-Bibliotheksversionen abhängig zu sein.

Zu Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive) migrieren

Aktualisieren Sie Ihre Jetpack Tile-Bibliotheken, um die neuesten Verbesserungen zu nutzen, darunter UI-Änderungen, die dafür sorgen, dass sich Ihre Kacheln nahtlos in das System einfügen.

Jetpack Tiles 1.5 und Jetpack ProtoLayout 1.3 führen mehrere wichtige Verbesserungen und Änderungen ein. Dazu gehören:

  • Eine Compose-ähnliche API zum Beschreiben der Benutzeroberfläche.
  • Material 3 Expressive-Komponenten, darunter der am unteren Rand fixierte Edge-Button und Unterstützung für verbesserte Grafiken: Lottie-Animationen, mehr Gradiententypen und neue Bogenlinienstile. – Hinweis: Einige dieser Funktionen können auch ohne Migration zur neuen API verwendet werden.

Empfehlungen

  • Alle Kacheln gleichzeitig migrieren: Vermeiden Sie es, Kachelversionen in Ihrer App zu mischen. Die Material 3-Komponenten befinden sich zwar in einem separaten Artefakt (androidx.wear.protolayout:protolayout-material3), sodass es technisch möglich ist, sowohl M2.5- als auch M3-Kacheln in derselben App zu verwenden. Wir raten jedoch dringend davon ab, es sei denn, es ist unbedingt erforderlich (z. B. wenn Ihre App eine große Anzahl von Kacheln hat, die nicht alle gleichzeitig migriert werden können).
  • UX-Richtlinien für Kacheln einhalten Da Kacheln sehr strukturiert sind und auf Vorlagen basieren, sollten Sie die Designs in den vorhandenen Beispielen als Ausgangspunkt für Ihre eigenen Designs verwenden.
  • Auf verschiedenen Bildschirm- und Schriftgrößen testen: Tiles enthalten oft viele Informationen, sodass Text (insbesondere auf Schaltflächen) leicht überlaufen und abgeschnitten werden kann. Um dies zu minimieren, sollten Sie die vordefinierten Komponenten verwenden und umfangreiche Anpassungen vermeiden. Testen Sie die Kachel mit der Kachelvorschau in Android Studio und auf mehreren echten Geräten.

Migrationsprozess

Abhängigkeiten aktualisieren

Aktualisieren Sie zuerst die Datei build.gradle.kts. Aktualisieren Sie die Versionen und ändern Sie die protolayout-material-Abhängigkeit zu protolayout-material3, wie hier gezeigt:

// In build.gradle.kts

//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"

// Use these versions for M3.
val tilesVersion = "1.5.0-rc01"
val protoLayoutVersion = "1.3.0-rc01"

 dependencies {
     // Use to implement support for wear tiles
     implementation("androidx.wear.tiles:tiles:$tilesVersion")

     // Use to utilize standard components and layouts in your tiles
     implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")

     // Use to utilize components and layouts with Material Design in your tiles
     // implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
     implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")

     // Use to include dynamic expressions in your tiles
     implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")

     // Use to preview wear tiles in your own app
     debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")

     // Use to fetch tiles from a tile provider in your tests
     testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
 }

TileService bleibt weitgehend unverändert

Die wichtigsten Änderungen bei dieser Migration betreffen die UI-Komponenten. Daher sollte Ihre TileService-Implementierung, einschließlich aller Mechanismen zum Laden von Ressourcen, nur minimale oder gar keine Änderungen erfordern.

Die wichtigste Ausnahme betrifft die Aktivitätsaufzeichnung von Kacheln: Wenn Ihre App onTileEnterEvent() oder onTileLeaveEvent() verwendet, sollten Sie zu onRecentInteractionEventsAsync() migrieren. Ab API 36 werden diese Ereignisse in Batches zusammengefasst.

Code zur Layoutgenerierung anpassen

In ProtoLayout 1.2 (M2.5) gibt die Methode onTileRequest() eine TileBuilders.Tile zurück. Dieses Objekt enthielt verschiedene Elemente, darunter ein TimelineBuilders.Timeline, das wiederum das LayoutElement enthielt, das die Benutzeroberfläche der Kachel beschreibt.

In ProtoLayout 1.3 (M3) hat sich die allgemeine Datenstruktur und der Datenfluss nicht geändert. Die LayoutElement wird jetzt jedoch mit einem Compose-ähnlichen Ansatz erstellt, bei dem das Layout auf definierten Slots basiert. Diese sind (von oben nach unten) die titleSlot (optional; in der Regel für einen primären Titel oder Header), die mainSlot (erforderlich; für den Hauptinhalt) und die bottomSlot (optional; häufig für Aktionen wie eine Edge-Schaltfläche oder zusätzliche Informationen wie kurzen Text). Dieses Layout wird von der Funktion primaryLayout() erstellt.

Das Layout einer Kachel mit mainSlot, titleSlot und bottomSlot
Abbildung 1: Die Slots einer Kachel.
Vergleich der Layoutfunktionen von M2.5 und M3

M2.5

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
    PrimaryLayout.Builder(deviceConfiguration)
        .setResponsiveContentInsetEnabled(true)
        .setContent(
            Text.Builder(context, "Hello World!")
                .setTypography(Typography.TYPOGRAPHY_BODY1)
                .setColor(argb(0xFFFFFFFF.toInt()))
                .build()
        )
        .build()

M3

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
    materialScope(context, deviceConfiguration) {
        primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
    }

Die wichtigsten Unterschiede:

  1. Elimination of Builders. Das herkömmliche Builder-Muster für Material3-UI-Komponenten wird durch eine deklarativere, von Compose inspirierte Syntax ersetzt. (Auch für Nicht-UI-Komponenten wie String, Color und Modifier gibt es neue Kotlin-Wrapper.)
  2. Standardisierte Initialisierungs- und Layoutfunktionen: M3-Layouts basieren auf standardisierten Initialisierungs- und Strukturfunktionen: materialScope() und primaryLayout(). Diese obligatorischen Funktionen initialisieren die M3-Umgebung (Theming, Komponentenbereich über materialScope) und definieren das primäre slotbasierte Layout (über primaryLayout). Beide müssen genau einmal pro Layout aufgerufen werden.

Designs

Farbe

Ein herausragendes Merkmal von Material 3 Expressive ist das „dynamische Design“: Kacheln, die diese Funktion unterstützen (standardmäßig aktiviert), werden in einem vom System bereitgestellten Design angezeigt. Die Verfügbarkeit hängt vom Gerät und der Konfiguration des Nutzers ab.

Eine weitere Änderung in M3 ist die Erweiterung der Anzahl der Farb-Tokens von 4 auf 29. Die neuen Farb-Tokens finden Sie in der Klasse ColorScheme.

Typografie

Ähnlich wie bei M2.5 wird bei M3 stark auf vordefinierte Schriftgrößenkonstanten zurückgegriffen. Die direkte Angabe einer Schriftgröße wird nicht empfohlen. Diese Konstanten befinden sich in der Klasse Typography und bieten eine etwas erweiterte Auswahl an ausdrucksstärkeren Optionen.

Ausführliche Informationen finden Sie in der Dokumentation zur Typografie.

Form

Die meisten M3-Komponenten können sowohl in Form als auch in Farbe variieren.

Ein textButton (im mainSlot) mit der Form full:

Kachel mit „full“-Form (abgerundete Ecken)
Abbildung 2: Kachel mit „vollständiger“ Form

Derselbe TextButton mit der Form small:

Kachel mit der Form „Klein“ (weniger abgerundete Ecken)
Abbildung 3: Kachel mit der Form „Klein“

Komponenten

M3-Komponenten sind deutlich flexibler und konfigurierbarer als ihre M2.5-Pendants. Während für M2.5 oft unterschiedliche Komponenten für verschiedene visuelle Darstellungen erforderlich waren, wird bei M3 häufig eine verallgemeinerte, aber hochgradig konfigurierbare „Basis“-Komponente mit guten Standardeinstellungen verwendet.

Dieses Prinzip gilt für das „root“-Layout. In M2.5 war dies entweder ein PrimaryLayout oder ein EdgeContentLayout. In M3 wird nach der Einrichtung eines einzelnen MaterialScope auf oberster Ebene die Funktion primaryLayout() aufgerufen. Dadurch wird das Root-Layout direkt zurückgegeben (keine Builder erforderlich) und es werden LayoutElements für mehrere „Slots“ wie titleSlot, mainSlot und bottomSlot akzeptiert. Diese Slots können mit konkreten UI-Komponenten gefüllt werden, z. B. mit Komponenten, die von text(), button() oder card() zurückgegeben werden, oder mit Layoutstrukturen wie Row oder Column aus LayoutElementBuilders.

Themen sind eine weitere wichtige Verbesserung von M3. Standardmäßig entsprechen UI-Elemente automatisch den M3-Stilspezifikationen und unterstützen dynamische Designs.

M2.5 M3
Interaktive Elemente
Button oder Chip
Text
Text text()
Fortschrittsanzeigen
CircularProgressIndicator circularProgressIndicator() oder segmentedCircularProgressIndicator()
Layout
PrimaryLayout oder EdgeContentLayout primaryLayout()
buttonGroup()
Bilder
icon(), avatarImage() oder backgroundImage()

Modifikatoren

In M3 sind Modifiers, mit denen Sie eine Komponente dekorieren oder erweitern, Compose-ähnlicher. Diese Änderung kann den Boilerplate-Code reduzieren, da die entsprechenden internen Typen automatisch erstellt werden. Diese Änderung ist unabhängig von der Verwendung von M3-UI-Komponenten. Bei Bedarf können Sie Builder-Modifikatoren aus ProtoLayout 1.2 mit M3-UI-Komponenten verwenden und umgekehrt.

M2.5

// A Builder-style modifier to set the opacity of an element to 0.5
fun myModifier(): ModifiersBuilders.Modifiers =
    ModifiersBuilders.Modifiers.Builder()
        .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
        .build()

M3

// The equivalent Compose-like modifier is much simpler
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)

Sie können Modifizierer entweder im API-Stil erstellen oder die Erweiterungsfunktion toProtoLayoutModifiers() verwenden, um ein LayoutModifier in ein ModifiersBuilders.Modifier zu konvertieren.

Hilfsfunktionen

Mit ProtoLayout 1.3 können viele UI-Komponenten mit einer Compose-inspirierten API ausgedrückt werden. Für grundlegende Layout-Elemente wie rows und columns aus LayoutElementBuilders wird jedoch weiterhin das Builder-Muster verwendet. Um diese stilistische Lücke zu schließen und die Konsistenz mit den neuen M3-Komponenten-APIs zu fördern, sollten Sie Hilfsfunktionen verwenden.

Ohne Helfer

primaryLayout(
    mainSlot = {
        LayoutElementBuilders.Column.Builder()
            .setWidth(expand())
            .setHeight(expand())
            .addContent(text("A".layoutString))
            .addContent(text("B".layoutString))
            .addContent(text("C".layoutString))
            .build()
    }
)

Mit Helfern

// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
    Column.Builder().apply(builder).build()

primaryLayout(
    mainSlot = {
        column {
            setWidth(expand())
            setHeight(expand())
            addContent(text("A".layoutString))
            addContent(text("B".layoutString))
            addContent(text("C".layoutString))
        }
    }
)

Zu Tiles 1.2 / ProtoLayout 1.0 migrieren

Ab Version 1.2 befinden sich die meisten APIs für das Kachellayout im Namespace androidx.wear.protolayout. Wenn Sie die neuesten APIs verwenden möchten, führen Sie die folgenden Migrationsschritte in Ihrem Code aus.

Abhängigkeiten aktualisieren

Nehmen Sie in der Build-Datei Ihres App-Moduls die folgenden Änderungen vor:

Groovy

  // Remove
  implementation 'androidx.wear.tiles:tiles-material:version'

  // Include additional dependencies
  implementation "androidx.wear.protolayout:protolayout:1.3.0"
  implementation "androidx.wear.protolayout:protolayout-material:1.3.0"
  implementation "androidx.wear.protolayout:protolayout-expression:1.3.0"

  // Update
  implementation "androidx.wear.tiles:tiles:1.5.0"

Kotlin

  // Remove
  implementation("androidx.wear.tiles:tiles-material:version")

  // Include additional dependencies
  implementation("androidx.wear.protolayout:protolayout:1.3.0")
  implementation("androidx.wear.protolayout:protolayout-material:1.3.0")
  implementation("androidx.wear.protolayout:protolayout-expression:1.3.0")

  // Update
  implementation("androidx.wear.tiles:tiles:1.5.0")

Namespaces aktualisieren

Nehmen Sie in den Kotlin- und Java-basierten Codedateien Ihrer App die folgenden Änderungen vor. Alternativ können Sie dieses Script zum Umbenennen von Namespaces ausführen.

  1. Ersetzen Sie alle androidx.wear.tiles.material.*-Importe durch androidx.wear.protolayout.material.*. Führen Sie diesen Schritt auch für die androidx.wear.tiles.material.layouts-Mediathek aus.
  2. Ersetzen Sie die meisten anderen androidx.wear.tiles.*-Importe durch androidx.wear.protolayout.*.

    Die Importe für androidx.wear.tiles.EventBuilders, androidx.wear.tiles.RequestBuilders, androidx.wear.tiles.TileBuilders und androidx.wear.tiles.TileService sollten gleich bleiben.

  3. Einige eingestellte Methoden aus den Klassen „TileService“ und „TileBuilder“ wurden umbenannt:

    1. TileBuilders: getTimeline() bis getTileTimeline() und setTimeline() bis setTileTimeline()
    2. TileService – alter Preis: onResourcesRequest(), neuer Preis: onTileResourcesRequest()
    3. RequestBuilders.TileRequest: getDeviceParameters() bis getDeviceConfiguration(), setDeviceParameters() bis setDeviceConfiguration(), getState() bis getCurrentState() und setState() bis setCurrentState()