Pierwsze kroki z kafelkami

Aby zacząć udostępniać kafelki z aplikacji, dodaj te zależności do pliku build.gradle aplikacji.

Groovy

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

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

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

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

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

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

Kotlin

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

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

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

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

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

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

Kluczowych pojęć

Kafelki nie są tworzone w taki sam sposób jak aplikacje na Androida i korzystają z innych koncepcji:

  • Szablony układu: określają ogólne rozmieszczenie elementów wizualnych na ekranie. Płytka używa szablonu układu EdgeContentLayout, który zawiera wskaźnik postępu na krawędzi wyświetlacza, lub PrimaryLayout, który nie wyświetla tego wskaźnika.
  • Elementy układu: reprezentują pojedynczy element graficzny, np. Button lub Chip, albo kilka takich elementów zgrupowanych za pomocą elementu Column, MultiButtonLayout, MultiSlotLayout lub podobnego. Są one umieszczane w szablonie układu.
  • Zasoby: obiekty ResourceBuilders.Resources składają się z mapy par klucz-wartość zasobów Androida (obrazów) wymaganych do renderowania układu oraz wersji.
  • Harmonogram: obiekt TimelineBuilders.Timeline to lista co najmniej 1 wystąpienia obiektu układu. Możesz stosować różne mechanizmy i wyrażenia, aby wskazać, kiedy renderowanie powinno przejść z jednego obiektu układu na inny, np. aby w określonym momencie przestać wyświetlać dany układ.
  • Stan: struktura danych typu StateBuilders.State przekazywana między kafelkiem a aplikacją, aby umożliwić komunikację tych dwóch komponentów. Jeśli np. użytkownik kliknie przycisk na kafelku, stan będzie zawierać identyfikator tego przycisku. Typy danych możesz też wymieniać za pomocą mapy.
  • Płytka: obiekt TileBuilders.Tile reprezentujący płytkę, który składa się z osi czasu, identyfikatora wersji zasobów, interwału świeżości i stanu.
  • Protolayout: ten termin pojawia się w nazwach różnych klas związanych z płytkami i odnosi się do biblioteki Protolayout na Wear OS, czyli biblioteki graficznej używanej na różnych platformach Wear OS.

Tworzenie kafelka

Aby udostępnić kafelek z aplikacji, zaimplementuj usługę typu TileService i zarejestruj ją w pliku manifestu. W tym celu system wysyła żądania dotyczące niezbędnych płytek podczas wywołań onTileRequest()zasobów podczas wywołań onTileResourcesRequest().

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

Następnie dodaj usługę w tagu <application> pliku AndroidManifest.xml.

<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>

Filtr uprawnień i zamiarów rejestruje tę usługę jako dostawcę kafelka.

Ikona, etykieta, opis i zasob do podglądu są wyświetlane użytkownikowi, gdy konfiguruje on karty na telefonie lub zegarku.

Wprowadź aplikację i dodaj kartę do karuzeli kart (jest też bardziej przyjazny dla dewelopera sposób wyświetlania podglądu karty, ale na razie wykonaj to ręcznie).

Rysunek 1. Płytka „Hello World”.

Pełny przykład znajdziesz w przykładowym kodzie na GitHubie lub w laboratorium kodu.

Tworzenie interfejsu dla płytek

Układ kafelka jest zapisywany za pomocą wzoru kreatora. Układ kafelka jest zbudowany jak drzewo, które składa się z kontenerów układu i podstawowych elementów układu. Każdy element układu ma właściwości, które możesz ustawiać za pomocą różnych metod settera.

Podstawowe elementy układu

Obsługiwane są te elementy wizualne z biblioteki protolayout: Komponenty materiałowe:

  • Text: renderuje ciąg tekstowy, opcjonalnie z zawijaniem.
  • Image: renderowanie obrazu.
  • Spacer: zapewnia odstęp między elementami lub może pełnić funkcję separatora po ustawieniu koloru tła.

Komponenty materiału

Oprócz elementów podstawowych biblioteka protolayout-material zawiera komponenty, które zapewniają projekt kafelka zgodny z zaleceniami interfejsu Material Design.

  • Button: klikalny okrągły element zawierający ikonę.
  • Chip: klikalny komponent w kształcie stadionu, który może zawierać maksymalnie 2 wiersze tekstu i opcjonalną ikonę.

  • CompactChip: klikalny komponent w kształcie stadionu, który mieści jeden wiersz tekstu.

  • TitleChip: klikalny element w kształcie stadionu podobny do Chip, ale o większej wysokości, aby pomieścić tekst tytułu.

  • CircularProgressIndicator: kołowy wskaźnik postępu, który można umieścić wewnątrz EdgeContentLayout, aby wyświetlać postęp na krawędziach ekranu.

Kontenery układu

Obsługiwane są te kontenery wraz z układami Material Design:

  • Row: elementy podrzędne są rozmieszczone poziomo, jeden pod drugim.
  • Column: elementy podrzędne są rozmieszczane pionowo, jeden pod drugim.
  • Box: elementy podrzędne są nakładane na siebie.
  • Arc: elementy podrzędne są rozmieszczone w kółku.
  • Spannable: pozwala zastosować określone FontStyles do sekcji tekstu, a także mieszać tekst z obrazami. Więcej informacji znajdziesz w artykule Spannables.

Każdy kontener może zawierać co najmniej 1 kontener podrzędny, który może być też kontenerem. Na przykład element Column może zawierać wiele elementów Row jako podrzędnych, co tworzy układ podobny do siatki.

Przykładem kafelka z układem kontenera i 2 elementami podrzędnymi może być:

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VERTICAL_ALIGN_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();
}

Układy materiału

Oprócz podstawowych układów biblioteka protolayout-material zawiera kilka układów z określonym układem, które służą do umieszczania elementów w określonych „miejscach”.

  • PrimaryLayout: umieszcza pojedyncze główne działanie CompactChip na dole, a nad nim treści wyśrodkowane.

  • MultiSlotLayout: umieszcza etykiety główne i dodatkowe z opcjonalnymi treściami pomiędzy nimi oraz opcjonalną etykietą CompactChip na dole.

  • MultiButtonLayout: ustawia zestaw przycisków rozmieszczonych zgodnie ze wskazówkami Material Design.

  • EdgeContentLayout: umieszcza treści na krawędzi ekranu, na przykład CircularProgressIndicator. Gdy używasz tego układu, zawartość jest automatycznie wyświetlana z odpowiednimi marginesami i wypełnieniem.

Łuki

Obsługiwane są te elementy potomne kontenera Arc:

  • ArcLine: powoduje wyświetlenie wygiętej linii wokół łuku.
  • ArcText: renderowanie zakrzywionego tekstu w elementach łukowych.
  • ArcAdapter: element podstawowego układu jest renderowany na łuku, narysowany wzdłuż łuku.

Więcej informacji znajdziesz w dokumentacji referencyjnej dotyczącej poszczególnych typów elementów.

Modyfikatory

Do każdego dostępnego elementu układu można opcjonalnie zastosować modyfikatory. Używaj tych modyfikatorów w tych celach:

  • Zmienianie wizualnego wyglądu układu. Możesz np. dodać tło, obramowanie lub wypełnienie do elementu układu.
  • Dodaj metadane dotyczące układu. Możesz na przykład dodać do elementu układu modyfikator semantyczny na potrzeby czytników ekranu.
  • Dodawanie funkcji. Możesz na przykład dodać do elementu układu modyfikator z możliwością kliknięcia, aby kafelek stał się interaktywny. Więcej informacji znajdziesz w artykule Interakcja z kartami.

Możemy na przykład dostosować domyślny wygląd i metadane Image, jak pokazano w tym przykładowym kodzie:

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

Rozszerzenia

Spannable to specjalny typ kontenera, który rozmieszcza elementy w sposób podobny do tekstu. Jest to przydatne, gdy chcesz zastosować inny styl tylko do jednego podciągu w większym bloku tekstu. Nie jest to możliwe w przypadku elementu Text.

Kontenerek Spannable jest wypełniony elementami podrzędnymi Span. Inne podrzędne lub zagnieżdżone instancje Spannable są niedozwolone.

Istnieją 2 rodzaje elementów podrzędnych Span:

  • SpanText: renderuje tekst w określonym stylu.
  • SpanImage: renderuje obraz w tekście.

Możesz na przykład umieścić kursywę w słowie „world” na kafelku „Hello world” i wstawić między słowami obraz, jak w tym przykładzie kodu:

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

Praca z zasobami

Płytki nie mają dostępu do żadnych zasobów aplikacji. Oznacza to, że nie możesz przekazać identyfikatora obrazu Androida do elementu układu Image i spodziewać się, że zostanie on rozwiązany. Zamiast tego zastąpij metodę onTileResourcesRequest() i ręcznie podaj zasoby.

Obrazy można przesłać w ramach metody onTileResourcesRequest() na 2 sposoby:

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