Erste Schritte mit Kacheln

Nehmen Sie die folgenden Abhängigkeiten in die Datei build.gradle Ihrer Anwendung auf, um mit der Bereitstellung von Kacheln aus Ihrer App zu beginnen.

Groovig

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

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

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

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

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

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

Kotlin

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

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

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

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

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

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.4.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:

Kotlin

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
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_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

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

Java

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

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

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   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 Kachelanbieter.

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

Verwende das Vorschau-Metadaten-Tag, um eine Vorschau der Kachel anzuzeigen, wenn du sie auf deinem Smartphone konfigurierst.

Übersicht über den Lebenszyklus des Tile-Dienstes

Nachdem du dein TileService in deinem App-Manifest erstellt und deklariert hast, kannst du auf die Statusänderungen des Kacheldienstes reagieren.

TileService ist ein gebundener Dienst. Ihre TileService wird als Ergebnis Ihrer Anwendungsanfrage gebunden oder wenn das System mit ihr kommunizieren muss. Ein typischer Lebenszyklus eines gebundenen Dienstes umfasst die folgenden vier Callback-Methoden: onCreate(), onBind(), onUnbind() und onDestroy(). Das System ruft diese Methoden jedes Mal auf, wenn der Dienst in eine neue Lebenszyklusphase eintritt.

Zusätzlich zu den Callbacks, die den Lebenszyklus des gebundenen Dienstes steuern, können Sie andere Methoden implementieren, die für den Lebenszyklus TileService spezifisch sind. Alle Kacheldienste müssen onTileRequest() und onTileResourcesRequest() implementieren, um auf Aktualisierungsanfragen vom System zu reagieren.

  • onTileAddEvent(): Diese Methode wird nur aufgerufen, wenn der Nutzer deine Kachel zum ersten Mal hinzufügt und wenn er sie entfernt und noch einmal hinzufügt. Dies ist der beste Zeitpunkt für eine einmalige Initialisierung.

    onTileAddEvent() wird nur aufgerufen, wenn der Satz von Kacheln neu konfiguriert wird, und nicht, 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 zu Ihnen gehörenden Kacheln abzurufen.

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

  • onTileEnterEvent(): Das System ruft diese Methode auf, wenn eine von diesem Anbieter bereitgestellte Kachel auf dem Bildschirm erscheint.

  • onTileLeaveEvent(): Das System ruft diese Methode auf, wenn eine von diesem Anbieter bereitgestellte Kachel auf dem Bildschirm nicht mehr sichtbar ist.

  • onTileRequest(): Diese Methode wird aufgerufen, wenn eine neue Zeitachse von diesem Anbieter angefordert wird.

  • onTileResourcesRequest(): Das System ruft diese Methode auf, wenn das System ein Ressourcen-Bundle von diesem Anbieter anfordert. Dies kann passieren, wenn eine Tile zum ersten Mal geladen wird oder wenn sich die Ressourcenversion ändert.

Abfrage, welche Tiles aktiv sind

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

UI für Kacheln erstellen

Das Layout einer Kachel wird mithilfe eines Builder-Musters geschrieben. Das Layout einer Kachel ist wie eine Baumstruktur aufgebaut, die aus Layoutcontainern und grundlegenden Layoutelementen besteht. Jedes Layoutelement hat Eigenschaften, die Sie mit verschiedenen Setter-Methoden festlegen können.

Grundlegende Layoutelemente

Die folgenden visuellen Elemente aus der Bibliothek protolayout werden zusammen mit den Material-Komponenten unterstützt:

  • Text: Rendert einen Textstring mit optionalem Zeilenumbruch.
  • Image: rendert ein Bild.
  • Spacer: Bietet einen Innenabstand zwischen Elementen oder kann als Trennelement dienen, wenn Sie die Hintergrundfarbe festlegen.

Materialkomponenten

Zusätzlich zu den Grundelementen enthält die protolayout-material-Bibliothek Komponenten, die für ein Kacheldesign gemäß den Material Design-Empfehlungen für die Benutzeroberfläche sorgen.

  • Button: anklickbare kreisförmige Komponente, die ein Symbol enthalten soll.
  • Chip: anklickbare Komponente in Form eines Stadiums, die bis zu zwei Textzeilen und ein optionales Symbol enthalten kann.

  • CompactChip: anklickbare Komponente in Form eines Stadions, die eine Textzeile enthalten soll.

  • TitleChip: anklickbare Komponente in Form eines Stadions, ähnlich wie Chip, aber eine größere Höhe für den Titeltext.

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

Layoutcontainer

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

  • Row: Ordnet untergeordnete Elemente horizontal nacheinander an.
  • Column: Ordnet untergeordnete Elemente vertikal nacheinander an.
  • Box: überlagert untergeordnete Elemente.
  • Arc: blendet untergeordnete Elemente in einem Kreis an.
  • Spannable: gilt speziell für FontStyles in Textabschnitten mit verschränkten Texten und Bildern. Weitere Informationen finden Sie unter Spannables.

Jeder Container kann ein oder mehrere untergeordnete Elemente enthalten, die wiederum Container sein können. Ein Column kann beispielsweise mehrere Row-Elemente als untergeordnete Elemente enthalten, was zu einem rasterähnlichen 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 Grundlayouts bietet die protolayout-material-Bibliothek einige optimierte Layouts, die dafür vorgesehen sind, Elemente in bestimmten „Slots“ zu platzieren.

  • PrimaryLayout: Dadurch wird eine einzelne primäre Aktion CompactChip unten platziert, der Inhalt darüber zentriert.

  • MultiSlotLayout: Positioniert primäre und sekundäre Labels mit optionalen Inhalten dazwischen und einem optionalen CompactChip unten.

  • MultiButtonLayout: Hiermit werden Schaltflächen positioniert, die nach Materialrichtlinien angeordnet sind.

  • EdgeContentLayout: platziert Inhalte am Rand eines Bildschirms, z. B. CircularProgressIndicator. Wenn Sie dieses Layout verwenden, werden die entsprechenden Ränder und Abstände automatisch auf den darin enthaltenen Inhalt angewendet.

Bögen

Die folgenden untergeordneten Arc-Container werden unterstützt:

  • ArcLine: Hiermit wird eine gebogene Linie um den Bogen herum gerendert.
  • ArcText: rendert den gebogenen Text im Bogen
  • ArcAdapter: rendert ein grundlegendes Layoutelement im Bogen, der tangens 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 folgende Zwecke:

  • Sie ändern das visuelle Erscheinungsbild des Layouts. Fügen Sie Ihrem Layoutelement z. B. einen Hintergrund, einen Rahmen oder einen Innenrand hinzu.
  • Fügen Sie Metadaten zum Layout hinzu. Fügen Sie beispielsweise Ihrem Layoutelement einen Semantikmodifikator hinzu, der mit Screenreadern verwendet werden soll.
  • Fügen Sie Funktionen hinzu. Fügen Sie Ihrem Layoutelement beispielsweise einen anklickbaren Modifikator hinzu, um Ihre Kachel interaktiv zu machen. Weitere Informationen findest du unter Mit Kacheln interagieren.

Beispielsweise können wir das Standarddesign und die Metadaten eines 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();
}

Spannables

Ein Spannable ist eine spezielle Art von Container, der Elemente ähnlich wie Text anstellt. Dies ist nützlich, wenn Sie nur auf einen Teilstring in einem größeren Textblock einen anderen Stil anwenden möchten, was mit dem Element Text nicht möglich ist.

Ein Spannable-Container wird mit untergeordneten Span gefüllt. Andere untergeordnete Elemente oder verschachtelte Spannable-Instanzen sind nicht zulässig.

Es gibt zwei Arten von untergeordneten Span-Elementen:

  • SpanText: Text mit einem bestimmten Stil wird gerendert.
  • SpanImage: rendert ein Bild inline mit Text.

Sie können beispielsweise „world“ in einer „Hello world“-Kachel kursiv formatieren und ein Bild zwischen den Wörtern 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 deiner App. Das bedeutet, dass du eine Android-Bild-ID nicht an ein Image-Layoutelement übergeben kannst und nicht erwarten kannst, dass das Problem aufgelöst wird. Überschreiben Sie stattdessen die Methode onTileResourcesRequest() und geben Sie alle Ressourcen manuell an.

Es gibt zwei Möglichkeiten, Bilder innerhalb der Methode onTileResourcesRequest() bereitzustellen:

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()
);
}