Versionsverwaltung für Ansichten

Auf Wear OS-Geräten werden Kacheln von zwei Hauptkomponenten mit unabhängiger Versionierung gerendert. Damit die App-Kacheln auf allen Geräten ordnungsgemäß funktionieren, ist es wichtig, diese zugrunde liegende Architektur zu verstehen.

  • Jetpack-Bibliotheken für Ansichten: 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 onTileRequest()-Aufruf des Systems 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 für die Verarbeitung von Nutzerinteraktionen verantwortlich. Die Version des Renderers wird nicht vom App-Entwickler gesteuert und kann von Gerät zu Gerät variieren, auch bei identischer Hardware.

Das Aussehen oder Verhalten einer Kachel kann sowohl von den Bibliotheksversionen von Jetpack Tiles in Ihrer App als auch von der ProtoLayout-Renderer-Version auf dem Gerät des Nutzers abhängen. So kann ein Gerät beispielsweise die Drehung oder die Anzeige von Herzfrequenzdaten unterstützen, ein anderes Gerät aber nicht.

In diesem Dokument wird beschrieben, 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 Versionen der Jetpack-Bibliothek migrieren.

Kompatibilität berücksichtigen

Wenn Sie eine Kachel erstellen möchten, die auf verschiedenen Geräten ordnungsgemäß funktioniert, sollten Sie Folgendes beachten:

Rendererversion erkennen

  • Verwenden Sie die Methode getRendererSchemaVersion() des DeviceParameters-Objekts, das an die Methode onTileRequest() übergeben wird. Diese Methode gibt die Haupt- und Nebenversionsnummern des ProtoLayout-Renderers auf dem Gerät zurück.
  • Sie können dann in Ihrer onTileRequest()-Implementierung bedingte Logik verwenden, um das Design oder Verhalten der Kachel basierend auf der erkannten Rendererversion anzupassen.
    • Wenn eine bestimmte Animation beispielsweise nicht unterstützt wird, können Sie stattdessen ein statisches Bild anzeigen.

Die @RequiresSchemaVersion-Anmerkung

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

Beispiel

override fun onTileRequest(
    requestParams: TileService.TileRequest
): ListenableFuture<Tile> {
    val rendererVersion =
        requestParams.deviceConfiguration.rendererSchemaVersion
    val tile = Tile.Builder()

    if (
        rendererVersion.major > 1 ||
            (rendererVersion.major == 1 && rendererVersion.minor >= 300)
    ) {
        // Use a feature supported in renderer version 1.300 or later
        tile.setTileTimeline(/* ... */ )
    } else {
        // Provide fallback content for older renderers
        tile.setTileTimeline(/* ... */ )
    }

    return Futures.immediateFuture(tile.build())
}

Mit verschiedenen Rendererversionen testen

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

Die Funktion „Vorschau für Ansichten“ in Android Studio verwendet einen Renderer, der in der Jetpack ProtoLayout-Bibliothek eingebettet ist, von der Ihr Code abhängt. Ein anderer Ansatz besteht darin, beim Testen von Ansichten verschiedene Jetpack-Bibliotheksversionen zu verwenden.

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

Aktualisieren Sie Ihre Jetpack Tile-Bibliotheken, um die neuesten Verbesserungen zu nutzen, einschließlich UI-Änderungen, damit sich Ihre Kacheln nahtlos in das System einbinden lassen.

Jetpack Tiles 1.5 und Jetpack ProtoLayout 1.3 bieten mehrere wichtige Verbesserungen und Änderungen. Dazu gehören:

  • Eine Compose-ähnliche API zum Beschreiben der Benutzeroberfläche.
  • Material 3 Expressive-Komponenten, einschließlich der Schaltfläche mit abgerundeten Ecken und Unterstützung für erweiterte visuelle Elemente: Lottie-Animationen, mehr Farbverlaufstypen und neue Bogenlinienstile. – Hinweis: Einige dieser Funktionen können auch ohne Migration zur neuen API verwendet werden.

Empfehlungen

  • Alle Kacheln gleichzeitig migrieren Verwenden Sie in Ihrer App keine unterschiedlichen Ansichten. 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, dies ist absolut notwendig, z. B. wenn Ihre App eine große Anzahl von Kacheln enthält, die nicht alle gleichzeitig migriert werden können.
  • Befolgen Sie die UX-Richtlinien für Ansichten mit Kacheln. Da Kacheln sehr strukturiert und vorlagenbasiert sind, können Sie die Designs in den vorhandenen Beispielen als Ausgangspunkt für Ihre eigenen Designs verwenden.
  • Testen Sie auf verschiedenen Bildschirmen und mit verschiedenen Schriftgrößen. Da sie oft viele Informationen enthalten, ist der Text (insbesondere auf Schaltflächen) anfällig für Überlauf und Ausschneiden. Verwenden Sie die vordefinierten Komponenten und vermeiden Sie umfangreiche Anpassungen, um dies zu minimieren. Testen Sie die Funktion mit der Vorschau für Ansichten 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 Abhängigkeit von protolayout-material 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 sollten für Ihre TileService-Implementierung, einschließlich aller Mechanismen zum Laden von Ressourcen, nur minimale oder gar keine Änderungen erforderlich sein.

Die Hauptausnahme betrifft das Erfassen von Kachelnaktivitäten: Wenn in Ihrer App onTileEnterEvent() oder onTileLeaveEvent() verwendet wird, sollten Sie zu onRecentInteractionEventsAsync() migrieren. Ab API 36 werden diese Ereignisse in Batches gesendet.

Code zur Layoutgenerierung anpassen

In ProtoLayout 1.2 (M2.5) gibt die Methode onTileRequest() einen 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.

Bei ProtoLayout 1.3 (M3) haben sich die Datenstruktur und der Fluss insgesamt nicht geändert. Die LayoutElement wird jetzt jedoch mit einem von Compose inspirierten Ansatz mit einem Layout erstellt, das auf definierten Slots basiert. Das sind (von oben nach unten) titleSlot (in der Regel für einen Haupttitel oder Header), mainSlot (für den Hauptinhalt) und bottomSlot (häufig für Aktionen wie eine seitliche Schaltfläche oder ergänzende Informationen wie kurzer Text). Dieses Layout wird mit der Funktion primaryLayout() erstellt.

Das Layout einer Kachel mit mainSlot, titleSlot und bottomSlot
Abbildung 1: Die Slots einer Kachel.
Vergleich der Layoutfunktionen 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) })
    }

So heben Sie die wichtigsten Unterschiede hervor:

  1. Entfernung von Buildern Das traditionelle Builder-Muster für Material3-UI-Komponenten wird durch eine deklarativere, von Compose inspirierte Syntax ersetzt. Auch nicht zur Benutzeroberfläche gehörende Komponenten wie String/Color/Modifier erhalten 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 (Design, Komponentenbereich über materialScope) und definieren das primäre slottingbasierte 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 aktivieren (standardmäßig aktiviert), werden im 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 Ausweitung der Anzahl der Farb-Token, die von 4 auf 29 erhöht wurde. Die neuen Farb-Tokens finden Sie in der Klasse ColorScheme.

Typografie

Ähnlich wie bei M2.5 werden in M3 vordefinierte Schriftgrößenkonstanten verwendet. 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.

Eine textButton (in der mainSlot) mit der Form full:

Kachel mit voller Form (abgerundetere Ecken)
Abbildung 2: Kachel mit „full“-Form

Dieselbe Textschaltfläche mit der Form small:

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

Komponenten

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

Dieses Prinzip gilt für das „Stamm“-Layout. In M2.5 war dies entweder ein PrimaryLayout oder ein EdgeContentLayout. In M3 wird nach dem Erstellen einer einzelnen MaterialScope der obersten Ebene die Funktion primaryLayout() aufgerufen. Dadurch wird das Stammlayout direkt zurückgegeben (keine Builder erforderlich) und LayoutElements wird für mehrere „Slots“ akzeptiert, z. B. titleSlot, mainSlot und bottomSlot. Diese Slots können mit bestimmten UI-Komponenten wie text(), button() oder card() oder Layoutstrukturen wie Row oder Column aus LayoutElementBuilders ausgefüllt werden.

Eine weitere wichtige M3-Erweiterung sind Themen. Standardmäßig entsprechen UI-Elemente automatisch den M3-Stilvorgaben 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 verzieren oder ergänzen, eher mit Compose vergleichbar. Durch diese Änderung kann Boilerplate reduziert werden, 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-Modifizierer aus ProtoLayout 1.2 mit M3-UI-Komponenten und umgekehrt verwenden.

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 Modifikatoren mit beiden API-Stilen erstellen und auch die Erweiterungsfunktion toProtoLayoutModifiers() verwenden, um ein LayoutModifier in ein ModifiersBuilders.Modifier umzuwandeln.

Hilfsfunktionen

Mit ProtoLayout 1.3 können viele UI-Komponenten mit einer von Compose inspirierten API ausgedrückt werden. Für grundlegende Layoutelemente wie Zeilen und Spalten aus LayoutElementBuilders wird weiterhin das Builder-Muster verwendet. Um diese stilistische Lücke zu schließen und für Einheitlichkeit mit den neuen M3-Komponenten-APIs zu sorgen, 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 Ansichten 1.2 / ProtoLayout 1.0 migrieren

Ab Version 1.2 befinden sich die meisten APIs für das Layout von Ansichten 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.2.1"
  implementation "androidx.wear.protolayout:protolayout-material:1.2.1"
  implementation "androidx.wear.protolayout:protolayout-expression:1.2.1"

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

Kotlin

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

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

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

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 Bibliothek androidx.wear.tiles.material.layouts aus.
  2. Ersetzen Sie die meisten anderen androidx.wear.tiles.*-Importe durch androidx.wear.protolayout.*.

    Importe für androidx.wear.tiles.EventBuilders, androidx.wear.tiles.RequestBuilders, androidx.wear.tiles.TileBuilders und androidx.wear.tiles.TileService sollten unverändert bleiben.

  3. Einige verworfene 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()