Cómo comenzar a usar tarjetas

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Para comenzar a proporcionar tarjetas de tu app, incluye las siguientes dependencias en el archivo build.gradle.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.2.0-alpha02"

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

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

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

Kotlin

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

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

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

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

Cómo crear una tarjeta

Para proporcionar una tarjeta de tu aplicación, crea una clase que extienda TileService e implementa los métodos, como se muestra en la siguiente muestra de código:

Kotlin

private val RESOURCES_VERSION = "1"
class MyTileService : TileService() {
    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTimeline(Timeline.Builder().addTimelineEntry(
                TimelineEntry.Builder().setLayout(
                    Layout.Builder().setRoot(
                        Text.Builder().setText("Hello world!").setFontStyle(
                            FontStyle.Builder().setColor(argb(0xFF000000)).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        ).build())

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

Java

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)
            .setTimeline(new Timeline.Builder()
                .addTimelineEntry(new TimelineEntry.Builder()
                    .setLayout(new Layout.Builder()
                        .setRoot(new Text.Builder()
                            .setText("Hello world!")
                            .setFontStyle(new FontStyle.Builder()
                                .setColor(argb(0xFF000000)).build()
                            ).build()
                        ).build()
                    ).build()
                ).build()
            ).build()
        );
   }

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

A continuación, agrega un servicio dentro de la etiqueta <application> del archivo AndroidManifest.xml.

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

El filtro de intents y permisos registra este servicio como un proveedor de tarjetas.

El ícono, la etiqueta y la descripción se muestran al usuario cuando este configura tarjetas en su teléfono o reloj.

Usa la etiqueta de metadatos de vista previa para mostrar una vista previa de la tarjeta cuando se configura en el teléfono.

Cómo crear una IU para tarjetas

El diseño de una tarjeta se escribe con un patrón de compilador. Este diseño se compila como un árbol que consta de contenedores y elementos de diseño básicos. Cada elemento de diseño tiene propiedades, que puedes configurar mediante varios métodos set.

Elementos de diseño básicos

Se admiten los siguientes elementos visuales:

  • Text: Renderiza una string de texto (opcionalmente con una unión).
  • Image: Renderiza una imagen.
  • Spacer: Proporciona padding entre los elementos o puede actuar como divisor cuando configuras el color de fondo.

Componentes de Material

Además de los elementos básicos, la biblioteca de tiles-material proporciona componentes que garantizan un diseño de tarjeta, de acuerdo con las recomendaciones de la interfaz de usuario de Material Design.

  • Button: Es un componente circular en el que se puede hacer clic y que se diseñó para contener un ícono.
  • Chip: Es un componente con forma de estadio en el que se puede hacer clic y que se diseñó para contener hasta dos líneas de texto y un ícono opcional.
  • CompactChip: Es un componente con forma de estadio en el que se puede hacer clic y que se diseñó para contener una línea de texto.
  • TitleChip: Es un componente con forma de estadio en el que se puede hacer clic, similar a Chip, pero con una altura mayor para adaptarse al texto del título.
  • CircularProgressIndicator: Es un indicador de progreso circular que se puede colocar dentro de un ProgressIndicatorLayout para mostrar el progreso en los bordes de la pantalla.

Contenedores de diseño

Se admiten los siguientes contenedores:

  • Row: Ubica los elementos secundarios de forma horizontal, uno tras otro.
  • Column: Ubica los elementos secundarios de forma vertical, uno tras otro.
  • Box: Superpone los elementos secundarios uno sobre otro.
  • Arc: Ubica los elementos secundarios en un círculo.
  • Spannable: Aplica el elemento FontStyles específico a secciones de texto junto con imágenes y texto intercalados. Para obtener más información, consulta Spannables.

Cada contenedor puede incluir uno o más elementos secundarios, que también pueden ser contenedores. Por ejemplo, un Column puede contener varios elementos Row como elementos secundarios, lo que genera un diseño similar a una cuadrícula.

A modo de ejemplo, una tarjeta con un diseño de contenedor y dos elementos de diseño secundarios podría verse de la siguiente manera:

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

Diseños de Material

Además de los diseños básicos, la biblioteca de tiles-material proporciona algunos diseños bien definidos para contener elementos en "ranuras" específicas.

  • PrimaryLayout: Ubica una sola acción principal de CompactChip en la parte inferior con el contenido centrado sobre esta.
  • MultiSlotLayout: Ubica las etiquetas primarias y secundarias con contenido opcional intermedio y un elemento CompactChip opcional en la parte inferior.
  • ProgressIndicatorLayout: Ubica un objeto CircularProgressIndicator alrededor de los bordes de la pantalla y el contenido determinado dentro.

Arcos

Se admiten los siguientes contenedores secundarios de Arc:

  • ArcLine: Renderiza una línea curva alrededor del arco.
  • ArcText: Renderiza el texto curvo en el arco.
  • ArcAdapter: Renderiza un elemento de diseño básico en el arco, dibujado en una tangente al arco.

Para obtener más información, consulta la documentación de referencia de cada uno de los tipos de elementos.

Modificadores

De manera opcional, a cada elemento de diseño disponible se le pueden aplicar modificadores. Usa estos modificadores con los siguientes fines:

  • Cambia la apariencia visual del diseño. Por ejemplo, agrega un fondo, un borde o un padding al elemento de diseño.
  • Agrega metadatos sobre el diseño. Por ejemplo, agrega un modificador de semántica a tu elemento de diseño para usarlo con lectores de pantalla.
  • Agrega funcionalidad. Por ejemplo, agrega un modificador en el que se pueda hacer clic a tu elemento de diseño para que tu tarjeta sea interactiva. Para obtener más información, consulta el artículo sobre interacciones con la tarjeta.

Por ejemplo, podemos personalizar el aspecto y los metadatos predeterminados de un Image, como se muestra en el siguiente ejemplo de código:

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 es un tipo especial de contenedor que presenta los elementos de manera similar al texto. Es útil cuando deseas aplicar un estilo diferente a una sola substring en un bloque de texto más grande, lo que no es posible con el elemento Text.

Un contenedor Spannable se llena con elementos secundarios Span. No se permiten otras instancias secundarias ni instancias Spannable anidadas.

Hay dos tipos de elementos Span secundarios:

  • SpanText: Renderiza texto con un estilo específico.
  • SpanImage: Renderiza una imagen intercalada con texto.

Por ejemplo, puedes escribir "world" en cursiva en una tarjeta "Hello world" e insertar una imagen entre las palabras, como se muestra en el siguiente ejemplo de código:

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

Cómo trabajar con recursos

Las tarjetas no tienen acceso a ningún recurso de tu app, lo que significa que no puedes pasar el ID de una imagen de Android a un elemento de diseño Image y esperar que se resuelva. En cambio, anula el método onResourcesRequest() y proporciona los recursos de forma manual.

Existen dos maneras de brindar imágenes dentro del método onResourcesRequest():

Kotlin

override fun onResourcesRequest(
    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> onResourcesRequest(
       @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()
);
}