Sur les appareils Wear OS, les vignettes sont affichées par deux composants clés avec une gestion des versions indépendante. Pour vous assurer que les tuiles de vos applications fonctionnent correctement sur tous les appareils, il est important de comprendre cette architecture sous-jacente.
- Bibliothèques Jetpack liées aux cartes : 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 construire un objet
TileBuilder.Tile
(structure de données représentant votre vignette) en réponse à l'appelonTileRequest()
du système. - Render de ProtoLayout : ce composant système est responsable du rendu de l'objet
Tile
sur 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 si le matériel est identique.
L'apparence ou le comportement d'une vignette peuvent 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 être compatible avec la rotation ou l'affichage des données de fréquence cardiaque, tandis qu'un autre ne l'est pas.
Ce document explique comment vous assurer que votre application est compatible avec différentes versions de la bibliothèque Tiles et du moteur ProtoLayout Renderer, et comment migrer vers des versions plus récentes de la bibliothèque Jetpack.
Tenir compte de la compatibilité
Pour créer une carte qui fonctionne correctement sur différents appareils, tenez compte de la prise en charge variable des fonctionnalités. Pour ce faire, vous pouvez utiliser deux stratégies principales : détecter les capacités du moteur de rendu au moment de l'exécution et fournir des solutions de remplacement intégrées.
Détecter les capacités du moteur de rendu
Vous pouvez modifier dynamiquement la mise en page de votre carte en fonction des fonctionnalités disponibles sur un appareil donné.
Détecter la version du moteur de rendu
- Utilisez la méthode
getRendererSchemaVersion()
de l'objetDeviceParameters
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.
Annotation @RequiresSchemaVersion
- L'annotation
@RequiresSchemaVersion
sur les méthodes ProtoLayout indique la version minimale du schéma de rendu requise pour que cette méthode se comporte comme indiqué dans la documentation (exemple).- L'appel d'une méthode qui nécessite une version du moteur de rendu supérieure à celle disponible sur l'appareil n'entraînera pas le plantage de votre application, mais pourrait entraîner la non-affichage du contenu ou l'ignorance de la fonctionnalité.
Exemple de détection de version
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() }
Fournir des créations de remplacement
Certaines ressources vous permettent de définir un remplacement directement dans le générateur. Cette méthode est souvent plus simple que la vérification de la version du moteur de rendu et constitue l'approche privilégiée lorsqu'elle est disponible.
Un cas d'utilisation courant consiste à fournir une image statique comme solution de remplacement pour une animation Lottie. Si l'appareil n'est pas compatible avec les animations Lottie, il affichera l'image statique à la place.
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()
Tester avec différentes versions du moteur de rendu
Pour tester vos tuiles sur différentes versions du moteur de rendu, déployez-les sur différentes versions de l'émulateur Wear OS. (Sur les appareils physiques, les mises à jour de ProtoLayout Renderer 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 spécifique du moteur de rendu.)
La fonctionnalité d'aperçu des blocs d'Android Studio utilise un moteur de rendu intégré à la bibliothèque Jetpack ProtoLayout dont votre code dépend. Une autre approche consiste donc à dépendre de différentes versions de la bibliothèque Jetpack lors du test des blocs.
Migrer vers Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)
Mettez à jour vos bibliothèques Jetpack Tiles pour profiter des dernières améliorations, y compris des modifications de l'UI qui permettent à vos Tiles de s'intégrer de manière fluide au système.
Jetpack Tiles 1.5 et Jetpack ProtoLayout 1.3 introduisent plusieurs améliorations et modifications notables. En voici quelques exemples :
- Une API de type Compose pour décrire l'UI.
- Composants Material 3 Expressive, y compris le bouton de bordure inférieure 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
- Migrez toutes vos cartes simultanément. Évitez de mélanger les versions de Tiles dans votre application. Bien que les composants Material 3 résident dans un artefact distinct (
androidx.wear.protolayout:protolayout-material3
), ce qui permet techniquement d'utiliser à la fois les Tiles 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 Tiles qui ne peuvent pas toutes être migrées en même temps). - Adoptez les conseils concernant l'expérience utilisateur des tuiles. Étant donné la nature hautement structurée et basée sur des modèles des cartes, utilisez les conceptions des exemples existants comme point de départ pour vos propres conceptions.
- Testez votre application sur différentes tailles d'écran et de police. Les tuiles sont souvent riches 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 minimiser ce problème, utilisez les composants prédéfinis et évitez les personnalisations importantes. Testez à l'aide de la fonctionnalité d'aperçu des blocs d'Android Studio, ainsi que sur plusieurs appareils réels.
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 indiqué :
// 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 en grande partie inchangé
Les principales modifications apportées lors de cette migration concernent les composants de l'UI. Par conséquent, votre implémentation de TileService
, y compris tous les mécanismes de chargement de ressources, devrait nécessiter peu de modifications, voire aucune.
La principale exception concerne le suivi de l'activité des tuiles : si votre application utilise onTileEnterEvent()
ou onTileLeaveEvent()
, vous devez migrer vers onRecentInteractionEventsAsync()
. À partir de l'API 36, ces événements seront regroupé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 à son tour contenait le LayoutElement
décrivant l'UI du bloc.
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) titleSlot
(facultatif, généralement pour un titre principal ou un en-tête), mainSlot
(obligatoire, pour le contenu principal) et bottomSlot
(facultatif, souvent pour des actions comme un bouton de bordure ou des informations supplémentaires comme un texte court). Cette mise en page est construite par la fonction primaryLayout()
.

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 :
- Élimination des créateurs. Le schéma de création traditionnel pour les composants d'UI Material3 est remplacé par une syntaxe plus déclarative inspirée de Compose. (Les composants non liés à l'UI, tels que String/Color/Modifiers, bénéficient également de nouveaux wrappers Kotlin.)
- Fonctions d'initialisation et de mise en page standardisées Les mises en page M3 s'appuient sur des fonctions d'initialisation et de structure standardisées :
materialScope()
etprimaryLayout()
. Ces fonctions obligatoires initialisent l'environnement M3 (thème, portée des composants viamaterialScope
) et définissent la mise en page principale basée sur les emplacements (viaprimaryLayout
). Les deux doivent être appelées exactement une fois par mise en page.
Personnalisation des thèmes
Couleur
L'une des principales caractéristiques de Material 3 Expressive est le "thème dynamique". Les tuiles qui activent cette fonctionnalité (activée par défaut) s'affichent dans le thème fourni par le système (disponibilité en fonction de l'appareil et de la configuration de l'utilisateur).
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 plus étendue 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 mainSlot
) avec la forme full
:

Le même textButton avec la forme small
:

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 différents traitements visuels, M3 utilise fréquemment un composant de "base" généralisé, mais hautement configurable, avec de bons paramètres 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 premier niveau est établi, la fonction primaryLayout()
est appelée. Cela renvoie directement la mise en page racine (aucun générateur 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 ceux renvoyés par text(), button() ou card(), ou des structures de mise en page, telles que Row
ou Column
de LayoutElementBuilders
.
Les thèmes représentent une autre amélioration clé de M3. Par défaut, les éléments d'interface utilisateur respectent automatiquement les spécifications de style M3 et sont compatibles avec les thèmes dynamiques.
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, les Modifiers
, que vous utilisez pour décorer ou améliorer un composant, ressemblent davantage à Compose. Cette modification peut réduire le code passe-partout en construisant automatiquement les types internes appropriés. (Cette modification est orthogonale à l'utilisation des composants d'UI M3. Si nécessaire, vous pouvez utiliser des modificateurs de style de compilateur à partir de ProtoLayout 1.2 avec des composants d'UI 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
Alors que ProtoLayout 1.3 permet d'exprimer de nombreux composants d'UI à 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 création. Pour combler ce fossé stylistique et favoriser la cohérence avec les nouvelles API de composants M3, pensez à 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 des 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
// Removeimplementation '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
// Removeimplementation("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")
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.
- Remplacez toutes les importations
androidx.wear.tiles.material.*
parandroidx.wear.protolayout.material.*
. Effectuez également cette étape pour la bibliothèqueandroidx.wear.tiles.material.layouts
. Remplacez la plupart des autres importations
androidx.wear.tiles.*
parandroidx.wear.protolayout.*
.Les importations pour
androidx.wear.tiles.EventBuilders
,androidx.wear.tiles.RequestBuilders
,androidx.wear.tiles.TileBuilders
etandroidx.wear.tiles.TileService
ne doivent pas changer.Renommez quelques méthodes obsolètes des classes TileService et TileBuilder :
TileBuilders
:getTimeline()
engetTileTimeline()
, etsetTimeline()
ensetTileTimeline()
TileService
:onResourcesRequest()
enonTileResourcesRequest()
RequestBuilders.TileRequest
:getDeviceParameters()
engetDeviceConfiguration()
,setDeviceParameters()
ensetDeviceConfiguration()
,getState()
engetCurrentState()
, etsetState()
ensetCurrentState()