Obsługa wersji płytek

Na urządzeniach z Wear OS kafelki są renderowane przez 2 główne komponenty z niezależnym numerowaniem wersji. Aby mieć pewność, że kafelki aplikacji działają prawidłowo na wszystkich urządzeniach, warto poznać tę architekturę.

  • Biblioteki związane z kafelkami Jetpack: te biblioteki (w tym Wear Tiles i Wear ProtoLayout) są osadzone w aplikacji, a Ty jako deweloper kontrolujesz ich wersje. Aplikacja używa tych bibliotek do tworzenia obiektu TileBuilder.Tile (struktury danych reprezentującej kafel) w odpowiedzi na wywołanie onTileRequest() systemu.
  • Renderowanie ProtoLayout: ten komponent systemu odpowiada za renderowanie obiektu Tile na wyświetlaczu i obsługę interakcji użytkownika. Wersja renderera nie jest kontrolowana przez dewelopera aplikacji i może się różnić na różnych urządzeniach, nawet tych z identycznym sprzętem.

Wygląd lub działanie kafelka może się różnić w zależności od wersji biblioteki Jetpack Tiles w aplikacji i wersji renderera ProtoLayout na urządzeniu użytkownika. Na przykład jedno urządzenie może obsługiwać obracanie lub wyświetlanie danych o tętnie, a inne nie.

Z tego dokumentu dowiesz się, jak zapewnić zgodność aplikacji z różnymi wersjami biblioteki Tiles i renderera ProtoLayout oraz jak przeprowadzić migrację do wyższych wersji biblioteki Jetpack.

Sprawdź zgodność

Aby utworzyć kafel, który będzie działać prawidłowo na różnych urządzeniach, weź pod uwagę różnice w obsłudze funkcji. Możesz to zrobić za pomocą 2 głównych strategii: wykrywania możliwości renderowania w czasie działania i zapewniania wbudowanych rozwiązań rezerwowych.

Wykrywanie możliwości renderowania

Możesz dynamicznie zmieniać układ kafelka w zależności od funkcji dostępnych na danym urządzeniu.

Wykrywanie wersji renderera

  • Użyj metody getRendererSchemaVersion() obiektu DeviceParameters przekazanego do metody onTileRequest(). Ta metoda zwraca numery wersji głównej i pomocniczej renderera ProtoLayout na urządzeniu.
  • Następnie możesz użyć logiki warunkowej w onTileRequest(), aby dostosować wygląd lub działanie kafelka na podstawie wykrytej wersji renderera.

Adnotacja @RequiresSchemaVersion

  • Adnotacja @RequiresSchemaVersion w metodach ProtoLayout wskazuje minimalną wersję schematu renderowania wymaganą, aby ta metoda działała zgodnie z dokumentacją (przykład).
    • Wywołanie metody, która wymaga wyższej wersji renderera niż jest dostępna na urządzeniu, nie spowoduje awarii aplikacji, ale może doprowadzić do tego, że treści nie będą wyświetlane lub funkcja zostanie zignorowana.

Przykład wykrywania wersji

val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion

val arcElement =
    // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500)
    // and so is supported by renderer versions 1.500 and greater
    if (
        rendererVersion.major > 1 ||
        (rendererVersion.major == 1 && rendererVersion.minor >= 500)
    ) {
        // Use DashedArcLine if the renderer supports it …
        DashedArcLine.Builder()
            .setLength(degrees(270f))
            .setThickness(8f)
            .setLinePattern(
                LayoutElementBuilders.DashedLinePattern.Builder()
                    .setGapSize(8f)
                    .setGapInterval(10f)
                    .build()
            )
            .build()
    } else {
        // … otherwise use ArcLine.
        ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build()
    }

Podaj wartości zastępcze

Niektóre zasoby umożliwiają zdefiniowanie rezerwy bezpośrednio w narzędziu do tworzenia. Jest to często prostsze niż sprawdzanie wersji renderera i jest preferowanym podejściem, gdy jest dostępne.

Częstym przypadkiem użycia jest udostępnianie statycznego obrazu jako rezerwy dla animacji Lottie. Jeśli urządzenie nie obsługuje animacji Lottie, zamiast niej wyświetli obraz statyczny.

val lottieImage =
    ResourceBuilders.ImageResource.Builder()
        .setAndroidLottieResourceByResId(
            ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie)
                .setStartTrigger(createOnVisibleTrigger())
                .build()
        )
        // Fallback if lottie is not supported
        .setAndroidResourceByResId(
            ResourceBuilders.AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.lottie_fallback)
                .build()
        )
        .build()

Testowanie z różnymi wersjami renderera

Aby przetestować kafelki w różnych wersjach renderera, wdróż je w różnych wersjach emulatora Wear OS. (Na urządzeniach fizycznych aktualizacje renderera ProtoLayout są dostarczane przez Sklep Play lub aktualizacje systemu. Nie można wymusić instalacji określonej wersji renderera).

Funkcja podglądu kafelków w Android Studio korzysta z renderera wbudowanego w bibliotekę Jetpack ProtoLayout, od której zależy Twój kod. Innym podejściem jest więc korzystanie z różnych wersji biblioteki Jetpack podczas testowania kafelków.

Migracja do kafelków 1.5 / ProtoLayout 1.3 (Material 3 Expressive)

Zaktualizuj biblioteki kafelków Jetpack, aby korzystać z najnowszych ulepszeń, w tym zmian w interfejsie, które zapewniają bezproblemową integrację kafelków z systemem.

Biblioteki Jetpack Tiles 1.5 i Jetpack ProtoLayout 1.3 wprowadzają kilka istotnych ulepszeń i zmian. Należą do nich:

  • Interfejs API podobny do Compose do opisywania interfejsu.
  • Komponenty Material 3 Expressive, w tym przycisk na dole ekranu i obsługa ulepszonych elementów wizualnych: animacji Lottie, większej liczby typów gradientów i nowych stylów linii łukowych. – Uwaga: z niektórych z tych funkcji można korzystać bez przechodzenia na nowy interfejs API.

Rekomendacje

  • Przenieś wszystkie karty jednocześnie. Unikaj mieszania wersji kafelków w aplikacji. Komponenty Material 3 znajdują się w osobnym artefakcie (androidx.wear.protolayout:protolayout-material3), co technicznie umożliwia używanie w tej samej aplikacji zarówno kafelków M2.5, jak i M3. Zdecydowanie odradzamy jednak takie podejście, chyba że jest to absolutnie konieczne (np. jeśli aplikacja ma dużą liczbę kafelków, których nie można od razu przenieść).
  • Postępuj zgodnie ze wskazówkami dotyczącymi interfejsu kafelków. Ze względu na wysoce ustrukturyzowany i oparty na szablonach charakter kafelków używaj projektów z istniejących przykładów jako punktów wyjścia dla własnych projektów.
  • Testuj na różnych rozmiarach ekranu i czcionki. Kafelki często zawierają dużo informacji, dlatego tekst (zwłaszcza na przyciskach) może się nie mieścić i być ucięty. Aby to zminimalizować, używaj gotowych komponentów i unikaj rozbudowanych dostosowań. Testuj za pomocą funkcji podglądu kafelka w Android Studio oraz na wielu rzeczywistych urządzeniach.

Proces migracji

Aktualizowanie zależności

Najpierw zaktualizuj plik build.gradle.kts. Zaktualizuj wersje i zmień zależność protolayout-material na protolayout-material3, jak pokazano poniżej:

// In build.gradle.kts

//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"

// Use these versions for M3.
val tilesVersion = "1.5.0-rc01"
val protoLayoutVersion = "1.3.0-rc01"

 dependencies {
     // Use to implement support for wear tiles
     implementation("androidx.wear.tiles:tiles:$tilesVersion")

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

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

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

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

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

Usługa TileService pozostaje w większości niezmieniona

Najważniejsze zmiany w tej migracji dotyczą komponentów interfejsu. W związku z tym Twoja implementacja TileService, w tym wszelkie mechanizmy wczytywania zasobów, powinna wymagać minimalnych lub żadnych modyfikacji.

Głównym wyjątkiem jest śledzenie aktywności kafelków: jeśli Twoja aplikacja korzysta z funkcji onTileEnterEvent() lub onTileLeaveEvent(), musisz przejść na onRecentInteractionEventsAsync(). Od interfejsu API w wersji 36 te zdarzenia będą przesyłane w pakietach.

Dostosowywanie kodu generowania układu

W ProtoLayout 1.2 (M2.5) metoda onTileRequest() zwraca wartość TileBuilders.Tile. Obiekt ten zawierał różne elementy, w tym TimelineBuilders.Timeline, który z kolei zawierał LayoutElement opisujący interfejs kafelka.

W przypadku ProtoLayout 1.3 (M3) ogólna struktura i przepływ danych nie uległy zmianie, ale element LayoutElement jest teraz tworzony w sposób inspirowany Compose, a układ jest oparty na zdefiniowanych slotach, które (od góry do dołu) to titleSlot (opcjonalny; zwykle dla głównego tytułu lub nagłówka), mainSlot (obowiązkowy; dla podstawowej treści) i bottomSlot (opcjonalny; często dla działań, takich jak przycisk na krawędzi lub dodatkowe informacje, np. krótki tekst). Ten układ jest tworzony przez funkcję primaryLayout().

Układ kafelka z wyświetlonymi elementami mainSlot, titleSlot i bottomSlot
Rysunek 1. miejsca na karcie.
Porównanie funkcji układów M2.5 i M3

M2,5

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
    PrimaryLayout.Builder(deviceConfiguration)
        .setResponsiveContentInsetEnabled(true)
        .setContent(
            Text.Builder(context, "Hello World!")
                .setTypography(Typography.TYPOGRAPHY_BODY1)
                .setColor(argb(0xFFFFFFFF.toInt()))
                .build()
        )
        .build()

M3

fun myLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
    materialScope(context, deviceConfiguration) {
        primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
    }

Aby podkreślić najważniejsze różnice:

  1. Eliminacja usług budowlanych Tradycyjny wzorzec konstruktora komponentów interfejsu Material3 został zastąpiony bardziej deklaratywną składnią inspirowaną Compose. (Nowe otoczki Kotlin otrzymują też komponenty inne niż interfejs, takie jak String, Color i Modifiers).
  2. Standardowe funkcje inicjowania i układu Układy M3 korzystają ze standardowych funkcji inicjowania i struktury: materialScope()primaryLayout(). Te obowiązkowe funkcje inicjują środowisko M3 (motywy, zakres komponentów za pomocą materialScope) i definiują podstawowy układ oparty na slotach (za pomocą primaryLayout). Obie funkcje muszą być wywoływane dokładnie raz na układ.

Motywy

Kolor

Wyróżniającą się cechą Material 3 Expressive jest „dynamiczne motywy”: kafelki, które umożliwiają tę funkcję (domyślnie włączoną), będą wyświetlane w motywie dostarczonym przez system (dostępność zależy od urządzenia i konfiguracji użytkownika).

Kolejną zmianą w M3 jest zwiększenie liczby tokenów kolorów z 4 do 29. Nowe tokeny kolorów znajdziesz w klasie ColorScheme.

Typografia

Podobnie jak M2.5, M3 w dużej mierze opiera się na wstępnie zdefiniowanych stałych rozmiaru czcionki – bezpośrednie określanie rozmiaru czcionki jest odradzane. Te stałe znajdują się w klasie Typography i oferują nieco szerszy zakres bardziej wyrazistych opcji.

Szczegółowe informacje znajdziesz w dokumentacji dotyczącej typografii.

Kształt

Większość komponentów M3 może się różnić kształtem i kolorem.

textButton (w mainSlot) o kształcie full:

Kafel z pełnym kształtem (bardziej zaokrąglone rogi)
Rysunek 2. Płytka o „pełnym” kształcie

Ten sam element TextButton w kształcie small:

Kafel o „małym” kształcie (mniej zaokrąglone rogi)
Rysunek 3. Kafel o „małym” kształcie

Komponenty

Komponenty M3 są znacznie bardziej elastyczne i konfigurowalne niż ich odpowiedniki M2.5. W przypadku M2.5 często wymagane były odrębne komponenty do różnych sposobów wyświetlania, natomiast M3 często wykorzystuje uogólniony, ale wysoce konfigurowalny komponent „bazowy” z dobrymi ustawieniami domyślnymi.

Ta zasada dotyczy układu „głównego”. W wersji M2.5 była to wersja PrimaryLayout lub EdgeContentLayout. W M3 po utworzeniu pojedynczego elementu najwyższego poziomu MaterialScope wywoływana jest funkcja primaryLayout(). Zwraca bezpośrednio układ główny (nie wymaga konstruktorów) i akceptuje LayoutElements w przypadku kilku „slotów”, takich jak titleSlot, mainSlotbottomSlot. Te miejsca mogą być wypełnione konkretnymi komponentami interfejsu, takimi jak te zwracane przez funkcje text(), button() lub card(), albo strukturami układu, takimi jak Row lub Column z funkcji LayoutElementBuilders.

Motywy to kolejne kluczowe ulepszenie M3. Domyślnie elementy interfejsu automatycznie dostosowują się do specyfikacji stylu M3 i obsługują dynamiczne motywy.

M2,5 M3
Elementy interaktywne
Button lub Chip
Text
Text text()
Wskaźniki postępu
CircularProgressIndicator circularProgressIndicator() lub segmentedCircularProgressIndicator()
Układ
PrimaryLayout lub EdgeContentLayout primaryLayout()
buttonGroup()
Obrazy
icon(), avatarImage() lub backgroundImage()

Modyfikatory

W M3 Modifiers, których używasz do dekorowania lub rozszerzania komponentu, są bardziej podobne do tych w Compose. Ta zmiana może zmniejszyć ilość kodu szablonowego przez automatyczne tworzenie odpowiednich typów wewnętrznych. (Ta zmiana jest niezależna od używania komponentów interfejsu M3. W razie potrzeby możesz używać modyfikatorów w stylu konstruktora z ProtoLayout 1.2 z komponentami interfejsu M3 i odwrotnie).

M2,5

// A Builder-style modifier to set the opacity of an element to 0.5
fun myModifier(): ModifiersBuilders.Modifiers =
    ModifiersBuilders.Modifiers.Builder()
        .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
        .build()

M3

// The equivalent Compose-like modifier is much simpler
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)

Modyfikatory możesz tworzyć w stylu interfejsu API. Możesz też użyć funkcji rozszerzenia toProtoLayoutModifiers(), aby przekształcić LayoutModifierModifiersBuilders.Modifier.

Funkcje pomocnicze

Chociaż ProtoLayout 1.3 umożliwia wyrażanie wielu komponentów interfejsu za pomocą interfejsu API inspirowanego Compose, podstawowe elementy układu, takie jak wierszekolumnyLayoutElementBuilders, nadal korzystają z wzorca konstruktora. Aby zniwelować tę różnicę stylistyczną i zapewnić spójność z nowymi interfejsami API komponentów M3, rozważ użycie funkcji pomocniczych.

Bez pomocy

primaryLayout(
    mainSlot = {
        LayoutElementBuilders.Column.Builder()
            .setWidth(expand())
            .setHeight(expand())
            .addContent(text("A".layoutString))
            .addContent(text("B".layoutString))
            .addContent(text("C".layoutString))
            .build()
    }
)

Z pomocnikami

// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
    Column.Builder().apply(builder).build()

primaryLayout(
    mainSlot = {
        column {
            setWidth(expand())
            setHeight(expand())
            addContent(text("A".layoutString))
            addContent(text("B".layoutString))
            addContent(text("C".layoutString))
        }
    }
)

Migracja do kart 1.2 / ProtoLayout 1.0

Od wersji 1.2 większość interfejsów API układu kafelków znajduje się w przestrzeni nazw androidx.wear.protolayout. Aby korzystać z najnowszych interfejsów API, wykonaj w kodzie te czynności związane z migracją.

Aktualizowanie zależności

W pliku kompilacji modułu aplikacji wprowadź te zmiany:

Groovy

  // Remove
  implementation 'androidx.wear.tiles:tiles-material:version'

  // Include additional dependencies
  implementation "androidx.wear.protolayout:protolayout:1.3.0"
  implementation "androidx.wear.protolayout:protolayout-material:1.3.0"
  implementation "androidx.wear.protolayout:protolayout-expression:1.3.0"

  // Update
  implementation "androidx.wear.tiles:tiles:1.5.0"

Kotlin

  // Remove
  implementation("androidx.wear.tiles:tiles-material:version")

  // Include additional dependencies
  implementation("androidx.wear.protolayout:protolayout:1.3.0")
  implementation("androidx.wear.protolayout:protolayout-material:1.3.0")
  implementation("androidx.wear.protolayout:protolayout-expression:1.3.0")

  // Update
  implementation("androidx.wear.tiles:tiles:1.5.0")

Aktualizowanie przestrzeni nazw

W plikach kodu aplikacji opartych na Kotlinie i Javie wprowadź te zmiany: Możesz też uruchomić ten skrypt zmiany nazwy przestrzeni nazw.

  1. Zastąp wszystkie importy androidx.wear.tiles.material.* elementem androidx.wear.protolayout.material.*. Wykonaj ten krok również w przypadku androidx.wear.tiles.material.layoutsbiblioteki.
  2. Zastąp większość innych importów androidx.wear.tiles.* importami androidx.wear.protolayout.*.

    Importy dla androidx.wear.tiles.EventBuilders, androidx.wear.tiles.RequestBuilders, androidx.wear.tiles.TileBuildersandroidx.wear.tiles.TileService powinny pozostać bez zmian.

  3. Zmieniono nazwy kilku wycofanych metod z klas TileService i TileBuilder:

    1. TileBuilders: getTimeline()getTileTimeline()setTimeline()setTileTimeline()
    2. TileService: z onResourcesRequest() na onTileResourcesRequest()
    3. RequestBuilders.TileRequest: getDeviceParameters()getDeviceConfiguration(), setDeviceParameters()setDeviceConfiguration(), getState()getCurrentState()setState()setCurrentState()