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.5.0-alpha04" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.3.0-alpha04" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.3.0-alpha04" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.3.0-alpha04" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0-alpha04" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.5.0-alpha04" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.5.0-alpha04") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.3.0-alpha04") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.3.0-alpha04") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.3.0-alpha04") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.5.0-alpha04") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.5.0-alpha04") }
Conceptos clave
Las tarjetas no se compilan de la misma manera que las apps para Android y usan diferentes conceptos:
- Plantillas de diseño: Definen la disposición general de los elementos visuales en la pantalla. Una tarjeta usa una plantilla de diseño
EdgeContentLayout
, que incluye un indicador de progreso alrededor del borde de la pantalla, o unPrimaryLayout
, que no muestra este indicador. - Elementos de diseño: Representan un elemento gráfico individual, como
Button
oChip
, o varios elementos de este tipo agrupados conColumn
,MultiButtonLayout
,MultiSlotLayout
o elementos similares. Se incorporan en una plantilla de diseño. - Recursos: Los objetos
ResourceBuilders.Resources
consisten en un mapa de pares clave-valor de los recursos de Android (imágenes) que se requieren para renderizar un diseño y una versión. - Cronología: Un objeto
TimelineBuilders.Timeline
es una lista de una o más instancias de un objeto de diseño. Puedes proporcionar varios mecanismos y expresiones para indicar cuándo el renderizador debe cambiar de un objeto de diseño a otro, por ejemplo, para dejar de mostrar un diseño en un momento específico. - Estado: Es una estructura de datos de tipo
StateBuilders.State
que se pasa entre la tarjeta y la app para permitir que los dos componentes se comuniquen entre sí. Por ejemplo, si se presiona un botón en la tarjeta, el estado contiene el ID del botón. También puedes intercambiar tipos de datos con un mapa. - Tile: Es un objeto
TileBuilders.Tile
que representa una tarjeta, que consta de un cronograma, un ID de versión de recursos, un intervalo de actualización y un estado. - Protolayout: Este término aparece en el nombre de varias clases relacionadas con tarjetas y hace referencia a la biblioteca de Protolayout de Wear OS, una biblioteca de gráficos que se usa en varias plataformas de Wear OS.
Cómo crear una tarjeta
Para proporcionar una tarjeta desde tu app, implementa un servicio de tipo TileService
y regístralo en tu manifiesto. A partir de esto, el sistema solicita las tarjetas necesarias durante las llamadas a onTileRequest()
y los recursos durante las llamadas a onTileResourcesRequest()
.
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_BODY1) .setColor(argb(0xFFFFFFFF.toInt())) .build() ) ) .build() ) override fun onTileResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture( Resources.Builder() .setVersion(RESOURCES_VERSION) .build() ) }
A continuación, agrega un servicio dentro de la etiqueta <application>
del archivo AndroidManifest.xml
.
<service android:name=".snippets.tile.MyTileService" android:label="@string/tile_label" android:description="@string/tile_description" android:icon="@mipmap/ic_launcher" 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, la descripción y el recurso de vista previa se muestran al usuario cuando este configura tarjetas en su teléfono o reloj.
Para ver un ejemplo completo, consulta la muestra de código en GitHub o el codelab.
Descripción general del ciclo de vida del servicio de tarjetas
Una vez que hayas creado y declarado tu TileService
en el manifiesto de tu app, podrás responder a los cambios de estado del servicio de tarjetas.
TileService
es un servicio vinculado. Tu TileService
se vincula como resultado de la solicitud de tu app o si el sistema necesita comunicarse con ella. Un ciclo de vida de servicio vinculado típico contiene los siguientes cuatro métodos de devolución de llamada: onCreate()
, onBind()
, onUnbind()
y onDestroy()
. El sistema invoca estos métodos cada vez que el servicio entra en una nueva fase del ciclo de vida.
Además de las devoluciones de llamada que controlan el ciclo de vida del servicio vinculado, puedes implementar otros métodos específicos del ciclo de vida de TileService
. Todos los servicios de tarjetas deben implementar onTileRequest()
y onTileResourcesRequest()
para responder a las solicitudes de actualización del sistema.
onTileAddEvent()
: El sistema llama a este método solo cuando el usuario agrega tu tarjeta por primera vez y si la quita y la vuelve a agregar. Este es el mejor momento para realizar cualquier inicialización única.Solo se llama a
onTileAddEvent()
cuando se reconfigura el conjunto de tarjetas, no cada vez que el sistema crea una tarjeta. Por ejemplo, cuando se reinicia o enciende el dispositivo, no se llama aonTileAddEvent()
para las tarjetas que ya se habían agregado. En su lugar, puedes usargetActiveTilesAsync()
para obtener una instantánea de las tarjetas que te pertenecen que están activas.onTileRemoveEvent()
: El sistema llama a este método solo si el usuario quita tu tarjeta.onTileEnterEvent()
: El sistema llama a este método cuando aparece en la pantalla una tarjeta proporcionada por este proveedor.onTileLeaveEvent()
: El sistema llama a este método cuando una tarjeta proporcionada por este proveedor desaparece de la vista en la pantalla.onTileRequest()
: El sistema llama a este método cuando solicita un nuevo cronograma a este proveedor.onTileResourcesRequest()
: El sistema llama a este método cuando solicita un conjunto de recursos a este proveedor. Esto puede suceder la primera vez que se carga una tarjeta o cada vez que cambia la versión del recurso.
Consulta qué tarjetas están activas
Las tarjetas activas son tarjetas que se agregaron para mostrarse en el reloj. Usa el método estático getActiveTilesAsync()
de TileService
para consultar qué tarjetas que pertenecen a tu app están activas.
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 de la biblioteca protolayout
, junto con los componentes de Material:
Text
: Renderiza una cadena 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 protolayout-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 aChip
, 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 unEdgeContentLayout
para mostrar el progreso en los bordes de la pantalla.
Contenedores de diseño
Se admiten los siguientes contenedores, junto con los diseños de Material:
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
: AplicaFontStyles
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(VERTICAL_ALIGN_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 protolayout-material
proporciona algunos diseños bien definidos para contener elementos en "ranuras" específicas.
PrimaryLayout
: Ubica una sola acción principal deCompactChip
en la parte inferior con el contenido centrado sobre esta.MultiSlotLayout
: Ubica las etiquetas primarias y secundarias con contenido opcional intermedio y un elementoCompactChip
opcional en la parte inferior.MultiButtonLayout
: Ubica un conjunto de botones organizados de acuerdo con los lineamientos de Material.EdgeContentLayout
: Ubica el contenido alrededor del borde de una pantalla, como un objetoCircularProgressIndicator
. Cuando usas este diseño, su contenido tiene los márgenes y el padding apropiados aplicados automáticamente.
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 la tarjeta sea interactiva. Para obtener más información, consulta Cómo interactuar con tarjetas.
Por ejemplo, podemos personalizar el aspecto y los metadatos predeterminados de un Image
, como se muestra en la siguiente muestra 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 subcadena 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 onTileResourcesRequest()
y proporciona los recursos de forma manual.
Existen dos maneras de brindar imágenes dentro del método onTileResourcesRequest()
:
- Proporciona un recurso de elementos de diseño con
setAndroidResourceByResId()
. - Proporciona una imagen dinámica como un elemento
ByteArray
consetInlineResource()
.
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() ); }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo migrar a espacios de nombres de ProtoLayout
ConstraintLayout
en Compose- Cómo crear tarjetas de Configuración rápida personalizadas para tu app