Erste Schritte mit Kacheln

Wenn Sie Kacheln aus Ihrer Anwendung bereitstellen möchten, fügen Sie die folgenden Abhängigkeiten in die Datei build.gradle der Anwendung ein.

Groovig

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

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

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

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

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

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

Kotlin

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

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

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

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

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

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

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

Als Nächstes fügen Sie einen Dienst in das <application>-Tag der Datei AndroidManifest.xml ein.

<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 registrieren diesen Dienst als Kachelanbieter.

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

Verwenden Sie das Vorschau-Metadaten-Tag, um eine Vorschau der Kachel anzuzeigen, wenn Sie sie auf Ihrem Smartphone konfigurieren.

Lebenszyklus des Tile-Dienstes

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

TileService ist ein gebundener Dienst. Ihr TileService ist aufgrund Ihrer Anwendungsanfrage oder wenn das System mit ihr kommunizieren muss, gebunden. Ein typischer Lebenszyklus von gebundenen Diensten enthält 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 weitere Methoden implementieren, die für den TileService-Lebenszyklus spezifisch sind. Alle Kacheldienste müssen onTileRequest() und onTileResourcesRequest() implementieren, um auf Aktualisierungsanfragen vom System zu antworten.

  • onTileAddEvent(): Das System ruft diese Methode nur auf, wenn der Nutzer die Kachel zum ersten Mal hinzufügt und wenn der Nutzer die Kachel 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() für die bereits hinzugefügten Kacheln nicht aufgerufen. Sie können stattdessen getActiveTilesAsync() verwenden, um eine Übersicht darüber zu erhalten, welche Kacheln zu Ihnen aktiv sind.

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

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

  • onTileLeaveEvent(): Diese Methode wird vom System aufgerufen, wenn eine von diesem Anbieter bereitgestellte Kachel auf dem Bildschirm nicht mehr sichtbar ist.

  • onTileRequest(): Das System ruft diese Methode auf, wenn das System eine neue Zeitachse von diesem Anbieter anfordert.

  • onTileResourcesRequest(): Das System ruft diese Methode auf, wenn das System ein Ressourcen-Bundle von diesem Anbieter anfordert. Dies kann beim ersten Laden einer Kachel oder beim Ändern der Ressourcenversion passieren.

Aktive Kacheln abfragen

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

Benutzeroberfläche 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 verfügt über Eigenschaften, die Sie mithilfe verschiedener Setter-Methoden festlegen können.

Grundlegende Layoutelemente

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

  • Text: rendert einen Textstring mit optionalem Zeilenumbruch.
  • Image: Damit wird ein Bild gerendert.
  • Spacer: Bietet einen Innenabstand zwischen Elementen oder kann beim Festlegen der Hintergrundfarbe als Trennlinie dienen.

Materialkomponenten

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

  • Button: anklickbare runde Komponente, die ein Symbol enthält
  • Chip: anklickbare stadiumförmige Komponente, die bis zu zwei Textzeilen und ein optionales Symbol enthalten kann.

  • CompactChip: anklickbare Komponente in Stadionform, die eine Textzeile enthält

  • TitleChip: anklickbare stadionförmige Komponente, ähnlich wie Chip, aber mit einer größeren Höhe für den Titeltext.

  • CircularProgressIndicator: runde Fortschrittsanzeige, die innerhalb einer EdgeContentLayout platziert werden kann, um den Fortschritt an den Bildschirmrändern anzuzeigen.

Layoutcontainer

Folgende Container sowie Materiallayouts werden unterstützt:

  • Row: Legt untergeordnete Elemente horizontal hintereinander an.
  • Column: Legt untergeordnete Elemente vertikal nacheinander an.
  • Box: Untergeordnete Elemente werden übereinandergelegt.
  • Arc: legt untergeordnete Elemente in einem Kreis an.
  • Spannable: Wendet FontStyles auf Textabschnitte an, zusammen mit verschachtelten Texten und Bildern. Weitere Informationen finden Sie unter Spannables.

Jeder Container kann ein oder mehrere untergeordnete Elemente enthalten, die wiederum Container sein können. Beispielsweise kann ein Column 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 grundlegenden Layouts bietet die protolayout-material-Bibliothek einige überzeugende Layouts, die darauf ausgelegt sind, Elemente in bestimmten Anzeigenflächen zu halten.

  • PrimaryLayout: platziert eine einzelne primäre Aktion CompactChip am unteren Rand, wobei der Inhalt darüber zentriert wird.

  • MultiSlotLayout: Positioniert primäre und sekundäre Labels mit optionalem Inhalt dazwischen und einem optionalen CompactChip am unteren Rand.

  • MultiButtonLayout: Positioniert eine Reihe von Schaltflächen, die gemäß den Materialrichtlinien angeordnet sind.

  • EdgeContentLayout: Hiermit werden Inhalte am Bildschirmrand (z. B. CircularProgressIndicator) positioniert. Bei Verwendung dieses Layouts werden die entsprechenden Ränder und die entsprechenden Abstände automatisch auf den Inhalt angewendet.

Bögen

Die folgenden untergeordneten Arc-Container werden unterstützt:

  • ArcLine: Damit wird eine kurvenförmige Linie um den Bogen herum gerendert.
  • ArcText: Damit wird gebogener Text im Bogen gerendert.
  • ArcAdapter: Damit wird ein grundlegendes Layoutelement im Bogen gerendert, das auf einer Tangente 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. Sie können diese Modifikatoren für folgende Zwecke verwenden:

  • das visuelle Erscheinungsbild des Layouts ändern. Sie können Ihrem Layoutelement z. B. einen Hintergrund, einen Rahmen oder einen Abstand hinzufügen.
  • Fügen Sie Metadaten zum Layout hinzu. Beispielsweise können Sie Ihrem Layoutelement einen Semantikmodifikator für die Verwendung mit Screenreadern hinzufügen.
  • Funktionen hinzufügen Sie können z. B. Ihrem Layoutelement einen anklickbaren Modifikator hinzufügen, um die Kachel interaktiv zu gestalten. Weitere Informationen findest du unter Mit Kacheln interagieren.

Beispielsweise können wir das Standarddesign und die Standardmetadaten 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 ein besonderer Containertyp, der Elemente ähnlich wie Text anordnet. Das 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 enthält untergeordnete Span. Andere untergeordnete 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: Damit wird ein Bild inline mit Text gerendert.

Sie können beispielsweise „world“ in einer Kachel „Hello world“ kursiv darstellen und zwischen den Wörtern ein Bild 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

Kacheln haben keinen Zugriff auf die Ressourcen Ihrer App. Das bedeutet, dass Sie eine Android-Bild-ID nicht an ein Image-Layoutelement übergeben können und erwarten, dass es aufgelöst wird. Überschreiben Sie stattdessen die Methode onTileResourcesRequest() und geben Sie alle Ressourcen manuell an.

Es gibt zwei Möglichkeiten, Bilder mit 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()
);
}