Premiers pas avec les cartes

Pour commencer à fournir des cartes à partir de votre application, incluez les dépendances suivantes dans le fichier build.gradle de votre application.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.4.0"

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

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

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

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

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

Kotlin

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

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

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

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

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

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

Créer une carte

Pour fournir une carte à partir de votre application, créez une classe qui étend TileService et implémentez les méthodes, comme indiqué dans l'exemple de code suivant :

Kotlin

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
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_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(Resources.Builder()
            .setVersion(RESOURCES_VERSION)
            .build()
        )
}

Java

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

Ajoutez ensuite un service à l'intérieur de la balise <application> de votre fichier AndroidManifest.xml.

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   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>

Le filtre d'intent et d'autorisation enregistre ce service en tant que fournisseur de cartes.

L'icône, le libellé et la description sont présentés à l'utilisateur lorsqu'il configure des cartes sur son téléphone ou sa montre.

Utilisez la balise de métadonnées d'aperçu pour afficher un aperçu de la carte lorsqu'elle est configurée sur votre téléphone.

Présentation du cycle de vie du service de cartes

Une fois que vous avez créé et déclaré votre TileService dans le fichier manifeste de votre application, vous peut répondre aux changements d'état du service de cartes.

TileService est un service lié. Par conséquent, votre TileService est lié de votre requête d'application ou si le système doit communiquer avec elle. Une configuration type Le cycle de vie du service bound-service contient les quatre méthodes de rappel suivantes: onCreate(), onBind(), onUnbind() et onDestroy() Le système appelle ces méthodes chaque fois que le service entre dans une nouvelle phase du cycle de vie.

En plus des rappels qui contrôlent le cycle de vie des services liés, vous pouvez implémenter d'autres méthodes spécifiques au cycle de vie d'une TileService. Toutes les vignettes services doivent implémenter onTileRequest() et onTileResourcesRequest() pour répondre aux demandes de mises à jour du système.

  • onTileAddEvent(): le système n'appelle cette méthode que lorsque l'utilisateur ajoute votre vignette pour la première fois, et si l'utilisateur supprime et ajoute votre vignette la vignette. Il s'agit du meilleur moment pour effectuer une initialisation unique.

    onTileAddEvent() n'est appelé que lorsque l'ensemble de tuiles est reconfiguré. et non à chaque fois qu'une carte est créée par le système. Par exemple, lorsque l’appareil est redémarré ou sous tension, onTileAddEvent() n'est pas appelé pour les cartes qui ont déjà été ajoutées. Vous pouvez utiliser getActiveTilesAsync() à la place pour obtenir un instantané des vignettes qui vous appartiennent qui sont actives.

  • onTileRemoveEvent(): le système n'appelle cette méthode que si l'utilisateur supprime votre carte.

  • onTileEnterEvent(): le système appelle cette méthode lorsqu'une carte fournies par ce fournisseur est affichée à l'écran.

  • onTileLeaveEvent(): le système appelle cette méthode lorsqu'une carte fournies par ce fournisseur ne sont plus visibles à l'écran.

  • onTileRequest(): le système appelle cette méthode lorsqu'il demande un nouveau calendrier à ce fournisseur.

  • onTileResourcesRequest(): le système appelle cette méthode lorsque le demande un groupe de ressources à ce fournisseur. Cela peut se produire lors du premier chargement d'une carte ou dès que la version de la ressource des modifications.

Interroger les vignettes actives

Les cartes actives sont des cartes qui ont été ajoutées pour être affichées sur la montre. Utilisez La méthode statique getActiveTilesAsync() de TileService pour interroger les cartes appartenant à votre application sont actives.

Créer une UI pour les cartes

La disposition d'une carte est écrite à l'aide d'un modèle de compilateur. La mise en page d'une carte est construite comme une arborescence constituée de conteneurs de mise en page et d'éléments de mise en page de base. Chaque élément de mise en page possède des propriétés que vous pouvez définir à l'aide de différentes méthodes setter.

Éléments de mise en page de base

Les éléments visuels suivants de la bibliothèque protolayout sont acceptés, ainsi que les composants Material :

  • Text : affiche une chaîne de texte, avec éventuellement un retour à la ligne.
  • Image : affiche une image.
  • Spacer : fournit une marge intérieure entre les éléments ou peut servir de séparateur lorsque vous définissez sa couleur d'arrière-plan.

Composants Material

Outre les éléments de base, la bibliothèque protolayout-material fournit des composants qui garantissent une conception de carte conforme aux recommandations de l'interface utilisateur de Material Design.

  • Button : composant cliquable circulaire conçu pour contenir une icône.
  • Chip : composant cliquable en forme de stade conçu pour contenir jusqu'à deux lignes de texte et éventuellement une icône.

  • CompactChip : composant cliquable en forme de stade conçu pour contenir une ligne de texte.

  • TitleChip : composant cliquable en forme de stade semblable à Chip, mais avec une hauteur supérieure pour accueillir le texte du titre.

  • CircularProgressIndicator : indicateur de progression circulaire pouvant être placé à l'intérieur d'un EdgeContentLayout pour afficher la progression autour des bords de l'écran.

Conteneurs de mise en page

Les conteneurs suivants sont acceptés, de même que les mises en page Material :

  • Row : dispose les éléments enfants horizontalement, les uns après les autres.
  • Column : dispose les éléments enfants verticalement, les uns après les autres.
  • Box : superpose les éléments enfants les uns au-dessus des autres.
  • Arc : dispose les éléments enfants dans un cercle.
  • Spannable : applique des FontStyles spécifiques aux sections de texte, avec entrelacement du texte et des images. Pour en savoir plus, consultez Spannables.

Chaque conteneur peut comporter un ou plusieurs enfants qui peuvent, à leur tour, être des conteneurs. Par exemple, un élément Column peut contenir plusieurs éléments Row en tant qu'enfants, ce qui se traduit par une mise en page en forme de grille.

Par exemple, une carte avec une mise en page de conteneur et deux éléments de mise en page enfants peut se présenter comme suit :

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

Mises en page Material

Outre les mises en page de base, la bibliothèque protolayout-material fournit quelques mises en page conçues pour contenir des éléments à des "emplacements" spécifiques.

  • PrimaryLayout : positionne une seule action principale CompactChip en bas, avec le contenu centré au-dessus.

  • MultiSlotLayout : positionne les libellés principaux et secondaires avec du contenu facultatif entre les deux, et un CompactChip facultatif en bas.

  • MultiButtonLayout : positionne un ensemble de boutons disposés selon les directives de Material.

  • EdgeContentLayout : positionne le contenu autour du bord de l'écran, par exemple CircularProgressIndicator. Lorsque vous utilisez cette mise en page, les marges et les marges intérieures appropriées sont automatiquement appliquées au contenu.

Arcs de cercle

Les conteneurs enfants Arc suivants sont acceptés :

  • ArcLine : affiche une ligne courbe autour de l'arc.
  • ArcText : affiche du texte incurvé dans l'arc.
  • ArcAdapter : affiche un élément de mise en page de base dans l'arc, tracé à une tangente par rapport à l'arc.

Pour en savoir plus, consultez la documentation de référence de chacun des types d'éléments.

Modificateurs

Des modificateurs peuvent éventuellement être appliqués à chaque élément de mise en page disponible. Utilisez ces modificateurs aux fins suivantes :

  • Modifier l'apparence visuelle de la mise en page. Par exemple, ajouter un arrière-plan, une bordure ou une marge intérieure à votre élément de mise en page.
  • Ajouter des métadonnées sur la mise en page. Par exemple, ajouter un modificateur de sémantique à votre élément de mise en page pour l'utiliser avec des lecteurs d'écran.
  • Ajouter des fonctionnalités. Par exemple, ajouter un modificateur cliquable à votre élément de mise en page pour rendre la carte interactive. Pour en savoir plus, consultez Interagir avec les cartes.

Nous pouvons, par exemple, personnaliser l'apparence et les métadonnées par défaut d'une Image, comme indiqué dans l'exemple de code suivant :

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

Spannables

Un Spannable est un type de conteneur spécial qui dispose les éléments de la même manière que le texte. Cela s'avère utile lorsque vous souhaitez appliquer un style différent à une seule sous-chaîne d'un bloc de texte plus grand, ce qui n'est pas possible avec l'élément Text.

Un conteneur Spannable est rempli d'éléments enfants Span. Les autres éléments enfants, ou instances Spannable imbriquées, ne sont pas autorisés.

Il existe deux types d'éléments enfants Span :

  • SpanText : affiche le texte avec un style spécifique.
  • SpanImage : affiche une image en l'alignant sur le texte.

Par exemple, vous pouvez mettre en italique le mot "world" dans une carte "Hello world" et insérer une image entre les mots, comme indiqué dans l'exemple de code suivant :

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

Utiliser des ressources

Les cartes n'ont accès à aucune des ressources de votre application. Cela signifie que vous ne pouvez pas transmettre un ID d'image Android à un élément de mise en page Image et vous attendre à ce qu'il soit résolu. Au lieu de cela, remplacez la méthode onTileResourcesRequest() et fournissez des ressources manuellement.

Pour fournir des images au sein de la méthode onTileResourcesRequest(), vous pouvez procéder de deux façons :

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