Gestion des versions des cartes

Sur les appareils Wear OS, les cartes sont affichées par deux composants clés avec une gestion des versions indépendante. Pour vous assurer que les cartes de vos applications fonctionnent correctement sur tous les appareils, il est important de comprendre cette architecture sous-jacente.

  • Bibliothèques liées aux cartes Jetpack: ces bibliothèques (y compris Wear Tiles et Wear ProtoLayout) sont intégrées à votre application, et vous, en tant que développeur, contrôlez leurs versions. Votre application utilise ces bibliothèques pour créer un objet TileBuilder.Tile (la structure de données représentant votre carte) en réponse à l'appel onTileRequest() du système.
  • ProtoLayout Renderer:ce composant système est responsable de l'affichage de l'objet Tile à l'écran et de la gestion des interactions utilisateur. La version du moteur de rendu n'est pas contrôlée par le développeur de l'application et peut varier d'un appareil à l'autre, même ceux dont le matériel est identique.

L'apparence ou le comportement d'une carte peut varier en fonction des versions de la bibliothèque Jetpack Tiles de votre application et de la version du moteur de rendu ProtoLayout sur l'appareil de l'utilisateur. Par exemple, un appareil peut prendre en charge la rotation ou l'affichage des données de fréquence cardiaque, et un autre non.

Ce document explique comment vous assurer que votre application est compatible avec différentes versions de la bibliothèque Tiles et du moteur de rendu ProtoLayout, et comment migrer vers des versions supérieures de la bibliothèque Jetpack.

Tenir compte de la compatibilité

Pour créer une carte qui fonctionne correctement sur une gamme d'appareils, vous devez tenir compte des points suivants.

Détecter la version du moteur de rendu

  • Utilisez la méthode getRendererSchemaVersion() de l'objet DeviceParameters transmis à votre méthode onTileRequest(). Cette méthode renvoie les numéros de version majeure et mineure du moteur de rendu ProtoLayout sur l'appareil.
  • Vous pouvez ensuite utiliser une logique conditionnelle dans votre implémentation onTileRequest() pour adapter la conception ou le comportement de votre carte en fonction de la version du moteur de rendu détectée.
    • Par exemple, si une animation spécifique n'est pas prise en charge, vous pouvez afficher une image statique à la place.

@RequiresSchemaVersion annotation

  • L'annotation @RequiresSchemaVersion sur les méthodes ProtoLayout indique la version minimale du schéma du moteur de rendu requise pour que cette méthode se comporte comme indiqué dans la documentation (exemple).
    • Bien que l'appel d'une méthode nécessitant une version de rendu supérieure à celle disponible sur l'appareil ne provoque pas le plantage de votre application, il peut entraîner l'absence d'affichage du contenu ou l'ignorer.

Exemple

override fun onTileRequest(
    requestParams: TileService.TileRequest
): ListenableFuture<Tile> {
    val rendererVersion =
        requestParams.deviceConfiguration.rendererSchemaVersion
    val tile = Tile.Builder()

    if (
        rendererVersion.major > 1 ||
            (rendererVersion.major == 1 && rendererVersion.minor >= 300)
    ) {
        // Use a feature supported in renderer version 1.300 or later
        tile.setTileTimeline(/* ... */ )
    } else {
        // Provide fallback content for older renderers
        tile.setTileTimeline(/* ... */ )
    }

    return Futures.immediateFuture(tile.build())
}

Tester avec différentes versions du moteur de rendu

Pour tester vos cartes sur différentes versions de moteur de rendu, déployez-les sur différentes versions de l'émulateur Wear OS. (Sur les appareils physiques, les mises à jour du moteur de rendu ProtoLayout sont fournies par le Play Store ou les mises à jour du système. Il n'est pas possible de forcer l'installation d'une version de rendu spécifique.)

La fonctionnalité d'aperçu des cartes d'Android Studio utilise un moteur de rendu intégré à la bibliothèque Jetpack ProtoLayout sur laquelle votre code dépend. Une autre approche consiste donc à dépendre de différentes versions de la bibliothèque Jetpack lors des tests des cartes.

Migrer vers Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)

Mettez à jour vos bibliothèques de cartes Jetpack pour profiter des dernières améliorations, y compris des modifications de l'interface utilisateur pour intégrer vos cartes de manière transparente au système.

Jetpack Tiles 1.5 et Jetpack ProtoLayout 1.3 apportent plusieurs améliorations et modifications notables. En voici quelques exemples :

  • API semblable à Compose pour décrire l'UI.
  • Composants Material 3 Expressive, y compris le bouton de bord inférieur et la prise en charge des visuels améliorés: animations Lottie, plus de types de dégradés et nouveaux styles de lignes d'arc. - Remarque: certaines de ces fonctionnalités peuvent également être utilisées sans migrer vers la nouvelle API.

Recommandations

  • Migrer toutes vos cartes simultanément Évitez de mélanger les versions de cartes dans votre application. Bien que les composants Material 3 résident dans un artefact distinct (androidx.wear.protolayout:protolayout-material3), ce qui rend techniquement possible d'utiliser à la fois des cartes M2.5 et M3 dans la même application, nous vous déconseillons vivement cette approche, sauf si elle est absolument nécessaire (par exemple, si votre application comporte un grand nombre de cartes qui ne peuvent pas toutes être migrées en même temps).
  • Suivez les consignes relatives à l'expérience utilisateur des cartes. Compte tenu de la nature hautement structurée et modélisée des cartes, utilisez les conceptions des exemples existants comme point de départ pour vos propres conceptions.
  • Testez sur différentes tailles d'écran et de police. Les cartes sont souvent denses en informations, ce qui rend le texte (en particulier lorsqu'il est placé sur des boutons) susceptible de déborder et d'être coupé. Pour réduire ce problème, utilisez les composants prédéfinis et évitez toute personnalisation étendue. Effectuez des tests à l'aide de la fonction d'aperçu des cartes d'Android Studio, ainsi que sur plusieurs appareils physiques.

Processus de migration

Mettre à jour les dépendances

Commencez par mettre à jour votre fichier build.gradle.kts. Mettez à jour les versions et remplacez la dépendance protolayout-material par protolayout-material3, comme illustré:

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

TileService reste largement inchangé

Les principales modifications de cette migration affectent les composants de l'interface utilisateur. Par conséquent, votre implémentation de TileService, y compris les mécanismes de chargement de ressources, ne devrait nécessiter que des modifications minimales, voire aucune.

La principale exception concerne le suivi de l'activité des cartes: si votre application utilise onTileEnterEvent() ou onTileLeaveEvent(), vous devez migrer vers onRecentInteractionEventsAsync(). À partir de l'API 36, ces événements seront groupés.

Adapter votre code de génération de mise en page

Dans ProtoLayout 1.2 (M2.5), la méthode onTileRequest() renvoie un TileBuilders.Tile. Cet objet contenait divers éléments, y compris un TimelineBuilders.Timeline, qui contenait à son tour le LayoutElement décrivant l'UI de la carte.

Avec ProtoLayout 1.3 (M3), bien que la structure et le flux de données globaux n'aient pas changé, le LayoutElement est désormais construit à l'aide d'une approche inspirée de Compose, avec une mise en page basée sur des emplacements définis, qui sont (de haut en bas) le titleSlot (généralement pour un titre ou un en-tête principal), le mainSlot (pour le contenu principal) et le bottomSlot (souvent pour des actions telles qu'un bouton de bord ou des informations supplémentaires telles qu'un texte court). Cette mise en page est créée par la fonction primaryLayout().

Mise en page d&#39;une carte affichant mainSlot, titleSlot et bottomSlot
Figure 1: Les emplacements d'une carte
Comparaison des fonctions de mise en page M2.5 et 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) })
    }

Pour mettre en évidence les principales différences:

  1. Suppression des outils de création Le modèle de constructeur traditionnel pour les composants d'interface utilisateur Material3 est remplacé par une syntaxe plus déclarative, inspirée de Compose. (Les composants autres que l'UI tels que String/Color/Modifiers reçoivent également de nouveaux wrappers Kotlin.)
  2. Fonctions d'initialisation et de mise en page standardisées Les mises en page M3 s'appuient sur des fonctions d'initialisation et de structuration standardisées: materialScope() et primaryLayout(). Ces fonctions obligatoires initialisent l'environnement M3 (thématisation, portée du composant via materialScope) et définissent la mise en page principale basée sur les emplacements (via primaryLayout). Les deux doivent être appelées exactement une fois par mise en page.

Personnalisation des thèmes

Couleur

Une fonctionnalité remarquable de Material 3 Expressive est la thématisation dynamique : les cartes qui activent cette fonctionnalité (activée par défaut) s'affichent dans le thème fourni par le système (la disponibilité dépend de l'appareil et de la configuration de l'utilisateur).

Autre modification apportée dans M3 : le nombre de jetons de couleur est passé de 4 à 29. Les nouveaux jetons de couleur se trouvent dans la classe ColorScheme.

Typographie

Comme M2.5, M3 s'appuie fortement sur des constantes de taille de police prédéfinies. Il est déconseillé de spécifier directement une taille de police. Ces constantes se trouvent dans la classe Typography et offrent une gamme légèrement élargie d'options plus expressives.

Pour en savoir plus, consultez la documentation sur la typographie.

Forme

La plupart des composants M3 peuvent varier en termes de forme et de couleur.

Un textButton (dans le mainSlot) avec la forme full:

Carte avec une forme &quot;complète&quot; (angles plus arrondis)
Figure 2: Mosaïque avec forme "full"

Même bouton de texte avec la forme small:

Carte de forme &quot;petite&quot; (angles moins arrondis)
Figure 3: Mosaïque de forme "petite"

Composants

Les composants M3 sont beaucoup plus flexibles et configurables que leurs homologues M2.5. Alors que M2.5 nécessitait souvent des composants distincts pour des traitements visuels variés, M3 utilise fréquemment un composant de base généralisé, mais hautement configurable, avec de bonnes valeurs par défaut.

Ce principe s'applique à la mise en page racine. Dans M2.5, il s'agissait d'un PrimaryLayout ou d'un EdgeContentLayout. Dans M3, une fois qu'un seul MaterialScope de niveau supérieur est établi, la fonction primaryLayout() est appelée. Cela renvoie directement la mise en page racine (aucun compilateur n'est nécessaire) et accepte LayoutElements pour plusieurs "emplacements", tels que titleSlot, mainSlot et bottomSlot. Ces emplacements peuvent être remplis avec des composants d'UI concrets (tels que renvoyés par text(), button() ou card()) ou des structures de mise en page, telles que Row ou Column à partir de LayoutElementBuilders.

Les thèmes constituent une autre amélioration majeure de M3. Par défaut, les éléments d'interface utilisateur respectent automatiquement les spécifications de style M3 et sont compatibles avec la thématisation dynamique.

M2.5 M3
Éléments interactifs
Button ou Chip
Texte
Text text()
Indicateurs de progression
CircularProgressIndicator circularProgressIndicator() ou segmentedCircularProgressIndicator()
Disposition
PrimaryLayout ou EdgeContentLayout primaryLayout()
buttonGroup()
Images
icon(), avatarImage() ou backgroundImage()

Modificateurs

Dans M3, Modifiers, que vous utilisez pour décorer ou améliorer un composant, ressemble davantage à Compose. Cette modification peut réduire le code standard en construisant automatiquement les types internes appropriés. (Cette modification est orthogonale à l'utilisation des composants d'interface utilisateur M3. Si nécessaire, vous pouvez utiliser des modificateurs de style de compilateur à partir de ProtoLayout 1.2 avec les composants d'interface utilisateur M3, et inversement.)

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)

Vous pouvez créer des modificateurs à l'aide de l'un ou l'autre style d'API. Vous pouvez également utiliser la fonction d'extension toProtoLayoutModifiers() pour convertir un LayoutModifier en ModifiersBuilders.Modifier.

Fonctions d'assistance

Bien que ProtoLayout 1.3 permette d'exprimer de nombreux composants d'interface utilisateur à l'aide d'une API inspirée de Compose, les éléments de mise en page de base tels que les lignes et les colonnes de LayoutElementBuilders continuent d'utiliser le modèle de compilateur. Pour combler cette lacune stylistique et favoriser la cohérence avec les nouvelles API de composants M3, envisagez d'utiliser des fonctions d'assistance.

Sans assistants

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

Avec assistants

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

Migrer vers Tiles 1.2 / ProtoLayout 1.0

Depuis la version 1.2, la plupart des API de mise en page de cartes se trouvent dans l'espace de noms androidx.wear.protolayout. Pour utiliser les dernières API, suivez la procédure de migration suivante dans votre code.

Mettre à jour les dépendances

Dans le fichier de compilation de votre module d'application, apportez les modifications suivantes :

Groovy

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

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

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

Kotlin

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

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

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

Mettre à jour les espaces de noms

Dans les fichiers de code basés sur Kotlin et Java de votre application, effectuez les mises à jour suivantes. Vous pouvez également exécuter ce script de modification du nom d'un espace de noms.

  1. Remplacez toutes les importations androidx.wear.tiles.material.* par androidx.wear.protolayout.material.*. Effectuez également cette étape pour la bibliothèque androidx.wear.tiles.material.layouts.
  2. Remplacez la plupart des autres importations androidx.wear.tiles.* par androidx.wear.protolayout.*.

    Les importations pour androidx.wear.tiles.EventBuilders, androidx.wear.tiles.RequestBuilders, androidx.wear.tiles.TileBuilders et androidx.wear.tiles.TileService ne doivent pas changer.

  3. Renommez quelques méthodes obsolètes des classes TileService et TileBuilder :

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