Erste Schritte mit Kacheln

Wenn Sie Kacheln über Ihre App bereitstellen möchten, fügen Sie der build.gradle-Datei Ihrer App die folgenden Abhängigkeiten hinzu.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.5.0-alpha05"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.3.0-alpha05"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.3.0-alpha05"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.3.0-alpha05"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0-alpha05"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.5.0-alpha05"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.5.0-alpha05")

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

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

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

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

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

Kachel erstellen

Wenn Sie eine Kachel aus Ihrer Anwendung bereitstellen möchten, erstellen Sie eine Klasse, die TileService erweitert, und implementieren Sie die Methoden, wie im folgenden Codebeispiel gezeigt:

class MyTileService : TileService() {

    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(
            Tile.Builder()
                .setResourcesVersion(RESOURCES_VERSION)
                .setTileTimeline(
                    Timeline.fromLayoutElement(
                        Text.Builder(this, "Hello World!")
                            .setTypography(Typography.TYPOGRAPHY_BODY1)
                            .setColor(argb(0xFFFFFFFF.toInt()))
                            .build()
                    )
                )
                .build()
        )

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(
            Resources.Builder()
                .setVersion(RESOURCES_VERSION)
                .build()
        )
}

Fügen Sie als Nächstes einen Dienst im <application>-Tag Ihrer AndroidManifest.xml-Datei hinzu.

<service
    android:name=".snippets.tile.MyTileService"
    android:label="@string/tile_label"
    android:description="@string/tile_description"
    android:icon="@mipmap/ic_launcher"
    android:exported="true"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
    <intent-filter>
        <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
    </intent-filter>

    <meta-data android:name="androidx.wear.tiles.PREVIEW"
        android:resource="@drawable/tile_preview" />
</service>

Der Berechtigungs- und Intent-Filter registriert diesen Dienst als Ansichtsanbieter.

Das Symbol, das Label, die Beschreibung und die Vorschau werden dem Nutzer angezeigt, wenn er Kacheln auf seinem Smartphone oder seiner Smartwatch konfiguriert.

Übersicht über den Lebenszyklus von Webkarten-Diensten

Nachdem Sie Ihre TileService in Ihrem App-Manifest erstellt und deklariert haben, können Sie auf Statusänderungen des Ansichtsdienstes reagieren.

TileService ist ein gebundener Dienst. Ihre TileService wird aufgrund Ihrer App-Anfrage oder wenn das System mit ihr kommunizieren muss, gebunden. Ein typischer Lebenszyklus eines gebundenen Dienstes umfasst die folgenden vier Rückrufmethoden: onCreate(), onBind(), onUnbind() und onDestroy(). Das System ruft diese Methoden jedes Mal auf, wenn der Dienst eine neue Lebenszyklusphase betritt.

Zusätzlich zu den Callbacks, die den Lebenszyklus des verknüpften Dienstes steuern, können Sie andere Methoden implementieren, die speziell für den TileService-Lebenszyklus gelten. Alle Kacheldienste müssen onTileRequest() und onTileResourcesRequest() implementieren, um auf Aktualisierungsanfragen des Systems zu reagieren.

  • onTileAddEvent(): Das System ruft diese Methode nur auf, wenn der Nutzer Ihre Kachel zum ersten Mal hinzufügt und wenn er sie wieder entfernt und hinzufügt. Dies ist der beste Zeitpunkt für eine einmalige Initialisierung.

    onTileAddEvent() wird nur aufgerufen, wenn die Ansicht neu konfiguriert wird, nicht jedes Mal, wenn eine Kachel vom System erstellt wird. Wenn das Gerät beispielsweise neu gestartet oder eingeschaltet wird, wird onTileAddEvent() nicht für die bereits hinzugefügten Kacheln aufgerufen. Sie können stattdessen getActiveTilesAsync() verwenden, um einen Snapshot der aktiven Ihren eigenen Kacheln zu erhalten.

  • onTileRemoveEvent(): Das System ruft diese Methode nur auf, wenn der Nutzer Ihre Kachel entfernt.

  • onTileEnterEvent(): Diese Methode wird vom System aufgerufen, wenn eine vom Anbieter bereitgestellte Kachel auf dem Bildschirm sichtbar wird.

  • onTileLeaveEvent(): Das System ruft diese Methode auf, wenn eine Kachel dieses Anbieters nicht mehr auf dem Bildschirm sichtbar ist.

  • onTileRequest(): Diese Methode wird vom System aufgerufen, wenn es einen neuen Zeitplan von diesem Anbieter anfordert.

  • onTileResourcesRequest(): Diese Methode wird vom System aufgerufen, wenn es ein Ressourcenpaket von diesem Anbieter anfordert. Das kann beim ersten Laden einer Kachel oder bei jeder Änderung der Ressourcenversion passieren.

Abfragen, welche Kacheln aktiv sind

Aktive Ansichten sind Ansichten, die zur Anzeige auf der Smartwatch hinzugefügt wurden. Verwenden Sie die statische Methode getActiveTilesAsync() von TileService, um abzufragen, welche Kacheln zu Ihrer App gehören und aktiv sind.

Benutzeroberfläche für Kacheln erstellen

Das Layout einer Kachel wird mit einem Builder-Muster geschrieben. Das Layout einer Kachel ist wie ein Baum aufgebaut, der aus Layoutcontainern und grundlegenden Layoutelementen besteht. Jedes Layoutelement hat Eigenschaften, die Sie über verschiedene Setzermethoden festlegen können.

Grundlegende Layoutelemente

Neben Materialkomponenten werden die folgenden visuellen Elemente aus der protolayout-Bibliothek unterstützt:

  • Text: Rendert einen Textstring, optional mit Umbruch.
  • Image: rendert ein Bild.
  • Spacer: Bietet einen Abstand zwischen Elementen oder kann als Trennlinie dienen, wenn Sie die Hintergrundfarbe festlegen.

Materialkomponenten

Neben den grundlegenden Elementen bietet die protolayout-material-Bibliothek Komponenten, die für ein Kachelndesign sorgen, das den Empfehlungen für die Material Design-Benutzeroberfläche entspricht.

  • Button: Klickbare kreisförmige Komponente, die ein Symbol enthalten soll.
  • Chip: Klickbare, stadionförmige Komponente, die bis zu zwei Textzeilen und ein optionales Symbol enthalten kann.

  • CompactChip: Klickbare, stadionförmige Komponente, die eine Textzeile enthalten kann.

  • TitleChip: Anklickbare, stadionförmige Komponente, ähnlich wie Chip, aber mit größerer Höhe für Titeltext.

  • CircularProgressIndicator: Kreisförmige Fortschrittsanzeige, die in einem EdgeContentLayout platziert werden kann, um den Fortschritt am Bildschirmrand anzuzeigen.

Layoutcontainer

Die folgenden Container werden zusammen mit Material-Layouts unterstützt:

  • Row: Hier werden untergeordnete Elemente horizontal nacheinander angeordnet.
  • Column:ordnet untergeordnete Elemente vertikal an, nacheinander.
  • Box: Hier werden untergeordnete Elemente übereinander gelegt.
  • Arc: Die untergeordneten Elemente werden in einem Kreis angeordnet.
  • Spannable: Hiermit werden bestimmte FontStyles auf Textabschnitte angewendet und Text und Bilder werden miteinander verschachtelt. Weitere Informationen finden Sie unter Spannbare Elemente.

Jeder Container kann ein oder mehrere untergeordnete Elemente enthalten, die selbst auch Container sein können. Ein Column kann beispielsweise mehrere Row-Elemente als untergeordnete Elemente enthalten, was zu einem gitterartigen Layout führt.

Eine Kachel mit einem Containerlayout und zwei untergeordneten Layoutelementen könnte beispielsweise so aussehen:

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Java

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

Material-Layouts

Zusätzlich zu den einfachen Layouts bietet die protolayout-material-Bibliothek einige vordefinierte Layouts, die Elemente in bestimmten „Slots“ aufnehmen.

  • PrimaryLayout: Posiert eine einzelne primäre Aktion CompactChip unten und zentriert den Inhalt darüber.

  • MultiSlotLayout: Primäre und sekundäre Labels werden mit optionalem Inhalt dazwischen und einem optionalen CompactChip unten platziert.

  • MultiButtonLayout: Posiert eine Reihe von Schaltflächen, die gemäß den Material Design-Richtlinien angeordnet sind.

  • EdgeContentLayout: Mit dieser Option können Sie Inhalte am Bildschirmrand platzieren, z. B. ein CircularProgressIndicator. Bei diesem Layout werden die entsprechenden Ränder und Abstände für den Inhalt automatisch angewendet.

Bögen

Die folgenden untergeordneten Elemente von Arc-Containern werden unterstützt:

  • ArcLine: Mit dieser Option wird eine gebogene Linie um den Bogen gerendert.
  • ArcText: Mit dieser Option wird Text im Bogen gerendert.
  • ArcAdapter: Mit diesem Befehl wird ein einfaches Layoutelement im Bogen gerendert, das tangential zum Bogen gezeichnet wird.

Weitere Informationen finden Sie in der Referenzdokumentation zu den einzelnen Elementtypen.

Modifikatoren

Auf jedes verfügbare Layoutelement können optional Modifikatoren angewendet werden. Verwenden Sie diese Modifikatoren für die folgenden Zwecke:

  • Ändern Sie das Erscheinungsbild des Layouts. Fügen Sie dem Layoutelement beispielsweise einen Hintergrund, einen Rahmen oder einen Abstand hinzu.
  • Fügen Sie Metadaten zum Layout hinzu. Fügen Sie Ihrem Layoutelement beispielsweise einen semantischen Modifikator für die Verwendung mit Screenreadern hinzu.
  • Funktionen hinzufügen Fügen Sie Ihrem Layoutelement beispielsweise einen anklickbaren Modifier hinzu, um die Kachel interaktiv zu machen. Weitere Informationen finden Sie unter Mit Kacheln interagieren.

So können wir beispielsweise das Standard-Aussehen und die Metadaten einer Image anpassen, wie im folgenden Codebeispiel gezeigt:

Kotlin

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

Java

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

Expandable-Anzeigen

Ein Spannable ist ein spezieller Containertyp, in dem Elemente ähnlich wie Text angeordnet werden. Das ist nützlich, wenn Sie nur einem Teilstring in einem größeren Textblock einen anderen Stil zuweisen möchten. Das ist mit dem Element Text nicht möglich.

Ein Spannable-Container ist mit Kindern gefüllt, die Span sind. Andere untergeordnete Elemente oder verschachtelte Spannable-Instanzen sind nicht zulässig.

Es gibt zwei Arten von Span-Unterelementen:

  • SpanText: Text wird in einem bestimmten Stil gerendert.
  • SpanImage: Mit diesem Tag wird ein Bild inline mit dem Text gerendert.

Sie können beispielsweise „Welt“ in einer Kachel „Hallo Welt“ kursiv formatieren und ein Bild zwischen die Wörter einfügen, wie im folgenden Codebeispiel gezeigt:

Kotlin

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

Java

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

Mit Ressourcen arbeiten

Ansichten haben keinen Zugriff auf die Ressourcen Ihrer App. Das bedeutet, dass Sie einer Image-Layout-Komponente keine Android-Bild-ID übergeben können und erwarten können, dass sie aufgelöst wird. Überschreiben Sie stattdessen die Methode onTileResourcesRequest() und geben Sie alle Ressourcen manuell an.

Es gibt zwei Möglichkeiten, Bilder in der onTileResourcesRequest()-Methode anzugeben:

Kotlin

override fun onTileResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

Java

@Override
protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}