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

Tworzenie kafelka

Aby udostępnić kafelek z aplikacji, utwórz klasę rozszerzającą klasę TileService i zaimplementuj metody, jak pokazano w tym przykładowym kodzie:

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

Omówienie cyklu życia usługi mapy

Po utworzeniu i zadeklarowaniu TileService w pliku manifestu aplikacji możesz reagować na zmiany stanu usługi kafelka.

TileService to powiązana usługa. Twoje TileService jest powiązane z Twoim żądaniem aplikacji lub jeśli system musi się z nią komunikować. Typowy cykl życia usługi powiązanej obejmuje 4 metody wywołania zwrotnego: onCreate(), onBind(), onUnbind()onDestroy(). System wywołuje te metody za każdym razem, gdy usługa wchodzi w nową fazę cyklu życia.

Oprócz wywołań zwrotnych, które kontrolują cykl życia powiązanej usługi, możesz też implementować inne metody związane z cyklem życia TileService. Wszystkie usługi kafelków muszą implementować onTileRequest()onTileResourcesRequest(), aby odpowiadać na żądania aktualizacji z systemu.

  • onTileAddEvent(): system wywołuje tę metodę tylko wtedy, gdy użytkownik po raz pierwszy dodaje kafelek i gdy usuwa go, a następnie ponownie dodaje. To najlepszy moment na wykonanie jednorazowej inicjalizacji.

    onTileAddEvent() jest wywoływany tylko wtedy, gdy zestaw kafelków jest przekonfigurowany, a nie za każdym razem, gdy system tworzy kafelek. Na przykład po ponownym uruchomieniu lub włączeniu urządzenia funkcja onTileAddEvent() nie jest wywoływana w przypadku kafelków, które zostały już dodane. Możesz użyć kodu getActiveTilesAsync(), aby uzyskać podgląd, które Twoje kafelki są aktywne.

  • onTileRemoveEvent(): system wywołuje tę metodę tylko wtedy, gdy użytkownik usunie Twoją kartę.

  • onTileEnterEvent(): system wywołuje tę metodę, gdy kafelek udostępniony przez tego dostawcę pojawi się na ekranie.

  • onTileLeaveEvent(): system wywołuje tę metodę, gdy kafelek udostępniony przez tego dostawcę znika z ekranu.

  • onTileRequest(): system wywołuje tę metodę, gdy prosi o nową oś czasu od tego dostawcy.

  • onTileResourcesRequest(): system wywołuje tę metodę, gdy prosi o pakiet zasobów od tego dostawcy. Może się to zdarzyć przy pierwszym wczytaniu kafelka lub za każdym razem, gdy zmienia się wersja zasobu.

Zapytanie o to, które kafelki są aktywne

Aktywne karty to karty, które zostały dodane do wyświetlania na zegarku. Aby sprawdzić, które kafelki należące do Twojej aplikacji są aktywne, użyj statycznej metody TileService getActiveTilesAsync().

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 Material Design:

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

Komponenty materiału

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

  • Button: klikalny komponent kołowy przeznaczony do umieszczania 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 1 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 elementu EdgeContentLayout, aby wyświetlać postęp na krawędziach ekranu.

Kontenery układu

Obsługiwane są te kontenery i układy Material:

  • Row: rozmieszcza elementy podrzędne poziomo, jeden pod drugim.
  • Column: rozmieszcza elementy podrzędne pionowo, jeden pod drugim.
  • Box: nakłada podrzędne elementy jeden na drugim.
  • Arc: rozmieszcza elementy podrzędne w kółku.
  • Spannable: stosuje się do konkretnych sekcji tekstu FontStylesoraz do przeplatania tekstu z obrazami. Więcej informacji znajdziesz w artykule Spannables (w języku angielskim).

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

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 na dole pojedyncze główne działanie CompactChip, 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: umieszcza zestaw przycisków rozmieszczonych zgodnie ze wskazówkami Material Design.

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

Łuki

Obsługiwane są te elementy potomne kontenera Arc:

  • ArcLine: powoduje wyrenderowanie wygiętej linii wokół łuku.
  • ArcText: renderuje zakrzywiony tekst w elementie Arc.
  • ArcAdapter: renderuje podstawowy element układu na łuku, narysowanym wzdłuż łuku dotyczącego.

Więcej informacji znajdziesz w dokumentacji 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 Span dziećmi. Inne elementy podrzędne ani zagnieżdżone elementy Spannable nie są dozwolone.

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ć w nawiasach kursywą wyraz „world” w płytce „Hello world” i wstawić między słowami obraz, jak pokazano 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()
);
}