1. Introducción
Las tarjetas de Wear OS brindan un acceso sencillo a la información y a las acciones que los usuarios necesitan para realizar tareas. Con solo deslizar el dedo sobre la cara de reloj, el usuario puede consultar el pronóstico actualizado o iniciar el cronómetro.
Una tarjeta se ejecuta como parte de la IU del sistema en lugar de ejecutarse en el propio contenedor de la aplicación. Usamos un Servicio para describir el diseño y el contenido de la tarjeta. Luego, la IU del sistema renderizará la tarjeta cuando sea necesario.
Actividades
Compilarás la tarjeta de una app de mensajería que muestra conversaciones recientes. Desde esa pantalla, el usuario puede pasar a tres tareas comunes:
- Abrir una conversación
- Redactar un mensaje nuevo
Qué aprenderás
En este codelab, aprenderás a escribir tu propia tarjeta de Wear OS, incluido lo siguiente:
- Cómo crear un
TileService
- Cómo probar una tarjeta en un dispositivo
- Cómo obtener una vista previa de la IU de una tarjeta en Android Studio
- Cómo desarrollar la IU para una tarjeta
- Cómo agregar imágenes
- Cómo controlar interacciones
Requisitos previos
- Conocimientos básicos sobre Kotlin
2. Prepárate
En este paso, configurarás tu entorno y descargarás un proyecto inicial.
Requisitos
- Actualización de funciones de Android Studio Koala | 2024.1.2 Canary 1 o versiones posteriores
- Emulador o dispositivo Wear OS
Si no estás familiarizado con el uso de Wear OS, lee esta guía rápida antes de comenzar. Se incluyen instrucciones para configurar un emulador de Wear OS y se describe cómo navegar por el sistema.
Cómo descargar el código
Si ya instalaste git, solo ejecuta el siguiente comando para clonar el código de este repositorio.
git clone https://github.com/android/codelab-wear-tiles.git cd codelab-wear-tiles
Si no tienes git, puedes hacer clic en el siguiente botón para descargar todo el código de este codelab:
Abre el proyecto en Android Studio
En la ventana "Welcome to Android Studio", selecciona Open an Existing Project o File > Open y selecciona la carpeta [Ubicación de descarga].
3. Cómo crear una tarjeta básica
El punto de entrada para una tarjeta es el servicio de mosaicos para mapas. En este paso, registrarás un servicio de este tipo y definirás un diseño para la tarjeta.
HelloWorldTileService
Una clase que implementa TileService
debe especificar dos métodos:
onTileResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources>
onTileRequest(requestParams: TileRequest): ListenableFuture<Tile>
El primer método devuelve un objeto Resources
que asigna IDs de cadenas a los recursos de imagen que usaremos en la tarjeta.
El segundo devuelve la descripción de una tarjeta, incluido su diseño. Aquí es donde definimos el diseño de la tarjeta y cómo se vinculan los datos a ella.
Abre HelloWorldTileService.kt
desde el módulo start
. Todos los cambios que hagas estarán en este módulo. También hay un módulo finished
si quieres ver el resultado de este codelab.
HelloWorldTileService
extiende SuspendingTileService
, un wrapper compatible con corrutinas de Kotlin de la biblioteca de tarjetas de Horologist. Horologist es un grupo de bibliotecas de Google que tiene como objetivo aportar a los desarrolladores de Wear OS un complemento con funciones que comúnmente requieren, pero que aún no están disponibles en Jetpack.
SuspendingTileService
proporciona dos funciones de suspensión, que son versiones de corrutinas equivalentes de las funciones de TileService
:
suspend resourcesRequest(requestParams: ResourcesRequest): Resources
suspend tileRequest(requestParams: TileRequest): Tile
Si deseas obtener más información sobre las corrutinas, consulta la documentación sobre corrutinas de Kotlin en Android.
HelloWorldTileService
aún no está completo. Debemos registrar el servicio en nuestro manifiesto y también proporcionar una implementación para tileLayout
.
Cómo registrar el servicio de tarjetas
Una vez que se registre el servicio de tarjetas en el manifiesto, se mostrará en la lista de tarjetas disponibles que puede agregar el usuario.
Agrega el <service>
dentro del elemento <application>
:
start/src/main/AndroidManifest.xml
<service
android:name="com.example.wear.tiles.hello.HelloWorldTileService"
android:icon="@drawable/ic_waving_hand_24"
android:label="@string/hello_tile_label"
android:description="@string/hello_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>
<!-- The tile preview shown when configuring tiles on your phone -->
<meta-data
android:name="androidx.wear.tiles.PREVIEW"
android:resource="@drawable/tile_hello" />
</service>
El ícono y la etiqueta se usan (como marcador de posición) cuando la tarjeta se carga por primera vez o si hay un error al momento de cargarla. Los metadatos al final definen una imagen de vista previa que se muestra en el carrusel cuando el usuario agrega una tarjeta.
Cómo definir un diseño para la tarjeta
HelloWorldTileService
tiene una función llamada tileLayout
con un TODO()
como cuerpo. Ahora vamos a reemplazar eso con una implementación en la que definimos el diseño de nuestra tarjeta:
start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt
fun tileLayout(
context: Context,
deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
message: String,
) =
materialScope(
context = context,
deviceConfiguration = deviceConfiguration,
allowDynamicTheme = false,
) {
primaryLayout(mainSlot = { text(message.layoutString) })
}
Ya creaste tu primera tarjeta de Wear OS. Instalemos esta tarjeta y veamos cómo se ve.
4. Cómo probar tu tarjeta en un dispositivo
Con el módulo de partida seleccionado en el menú desplegable de la configuración de ejecución, puedes instalar la app (el módulo start
) en tu dispositivo o emulador, y, luego, instalar manualmente la tarjeta, como lo haría un usuario.
Sin embargo, Android Studio tiene un atajo para hacerlo: si presionas el ícono "Run service" (▷) en el margen y, luego, seleccionas "Run 'HelloWorldTileService'", se instalará y ejecutará la tarjeta en un dispositivo conectado.
Selecciona "Run 'HelloWorldTileService'" para compilar y ejecutar la tarjeta en un dispositivo conectado. Debería verse como en la siguiente captura de pantalla.
El sistema proporciona el ícono de "mano saludando" que aparece en la parte superior de la pantalla. Para cambiarlo, modifica la propiedad android:icon
del elemento <service>
de la tarjeta en el manifiesto.
Para mayor comodidad, este proceso también creará una "configuración de ejecución" de "HelloWorldTileService" para uso futuro.
5. Cómo agregar funciones de vista previa
Podemos obtener una vista previa de la IU de tarjetas en Android Studio. Eso reduce el ciclo de retroalimentación cuando se desarrolla la IU, lo que aumenta la velocidad de desarrollo.
Agrega una vista previa de tarjeta para el elemento HelloWorldTileService
al final del archivo HelloWorldTileService.kt
.
start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt
@Preview(device = WearDevices.SMALL_ROUND, name = "Small Round")
@Preview(device = WearDevices.LARGE_ROUND, name = "Large Round")
internal fun helloLayoutPreview(context: Context): TilePreviewData {
return TilePreviewData {
TilePreviewHelper.singleTimelineEntryTileBuilder(
helloLayout(context, it.deviceConfiguration, "Hello, preview tile!")
)
.build()
}
}
Usa el modo de editor "Pantalla dividida" para obtener una vista previa de la tarjeta:
Ten en cuenta que la anotación @Composable
no se usa. Aunque las tarjetas usan la misma IU de vista previa que las Funciones de componibilidad, las tarjetas no usan Compose y no son componibles.
6. Cómo crear una tarjeta de mensajes
La tarjeta de mensajería que vamos a crear se parece más a una tarjeta del mundo real. A diferencia del ejemplo de HelloWorld, este muestra los componentes expresivos de Material 3, muestra imágenes y controla las interacciones para abrir la app.
MessagingTileService
MessagingTileService
extiende la clase SuspendingTileService
que vimos antes.
7. Cómo agregar componentes de la IU
La biblioteca de ProtoLayout proporciona componentes y diseños precompilados, lo que te permite crear tarjetas que contengan lo último en diseño expresivo de Material 3 para Wear OS.
Agrega la dependencia de Tiles Material a tu archivo build.gradle
:
start/build.gradle
implementation "androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion"
Agrega el código de diseño a la función tileLayout()
, como el cuerpo de la función materialScope()
. Esto crea un diseño de dos filas (con dos botones cada una) y un botón de borde.
Busca la línea "TODO() // Add primaryLayout()" y reemplázala por el siguiente código.
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
primaryLayout(
mainSlot = {
// This layout code assumes "contacts" contains at least 4 elements, for sample code
// that can handle an arbitrary number of contacts, and also shows different numbers
// of contacts based on the physical screen size, see
// <https://github.com/android/wear-os-samples/tree/main/WearTilesKotlin>.
Column.Builder()
.apply {
setWidth(expand())
setHeight(expand())
addContent(
buttonGroup {
buttonGroupItem { contactButton(contacts[0]) }
buttonGroupItem { contactButton(contacts[1]) }
}
)
addContent(DEFAULT_SPACER_BETWEEN_BUTTON_GROUPS)
addContent(
buttonGroup {
buttonGroupItem { contactButton(contacts[2]) }
buttonGroupItem { contactButton(contacts[3]) }
}
)
}
.build()
},
bottomSlot = {
textEdgeButton(
onClick = clickable(), // TODO: Launch new conversation activity
labelContent = { text("New".layoutString) },
)
},
)
La función contactButton()
en el mismo archivo crea los botones de contacto individuales. Si el contacto tiene una imagen asociada, esta aparecerá en el botón. De lo contrario, se usan las iniciales del contacto.
En este punto, es posible que notes que, aunque el diseño general es correcto, faltan las imágenes:
Verás lo mismo si implementas la tarjeta en un dispositivo:
En el siguiente paso, corregiremos las imágenes faltantes.
8. Cómo agregar imágenes
A un alto nivel, las tarjetas constan de dos elementos: un diseño (que hace referencia a recursos por IDs de cadenas) y los recursos en sí (que pueden ser imágenes).
Por el momento, nuestro código proporciona el diseño, pero no los recursos en sí. Para corregir la vista previa, debemos proporcionar los "recursos" de la imagen. Para ello, busca "TODO: Add onTileResourceRequest" y agrega el siguiente código como argumento nombrado adicional a TilePreviewData()
:
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
// Additional named argument to TilePreviewData
onTileResourceRequest = { resourcesRequest ->
Resources.Builder()
.setVersion(resourcesRequest.version)
.apply {
contacts.forEach {
if (it.avatarSource is AvatarSource.Resource) {
addIdToImageMapping(
it.imageResourceId(),
it.avatarSource.resourceId
)
}
}
}
.build()
}
Las imágenes ahora deberían aparecer en la vista previa:
Sin embargo, si la tarjeta se implementa en un dispositivo, faltarán las imágenes. Para solucionar este problema, reemplaza la función resourcesRequest()
en Service.kt
por lo siguiente:
start/src/main/java/com/example/wear/tiles/messaging/tile/Service.kt
override suspend fun resourcesRequest(
requestParams: ResourcesRequest
): Resources {
// resourceIds is a list of the ids we need to provide images for. If we're passed an empty
// list, set resourceIds to all resources.
val resourceIds =
requestParams.resourceIds.ifEmpty {
contacts.map { it.imageResourceId() }
}
// resourceMap maps (tile) resource ids to (Android) resource ids.
val resourceMap =
contacts
.mapNotNull {
when (it.avatarSource) {
is AvatarSource.Resource ->
it.imageResourceId() to
it.avatarSource.resourceId
else -> null
}
}
.toMap()
.filterKeys {
it in resourceIds
} // filter to only the resources we need
// Add images in the resourceMap to the Resources object, and return the result.
return Resources.Builder()
.setVersion(requestParams.version)
.apply {
resourceMap.forEach { (id, imageResource) ->
addIdToImageMapping(id, imageResource)
}
}
.build()
}
Ahora, las imágenes también se muestran cuando la tarjeta se implementa en un dispositivo:
En el siguiente paso, controlaremos los clics en cada uno de los elementos.
9. Cómo controlar interacciones
Una de las cosas más útiles que podemos hacer con una tarjeta es proporcionar accesos directos a los recorridos críticos del usuario. Esto es diferente del selector de aplicaciones, que solo abre la app. Aquí tenemos espacio para brindar accesos directos contextuales a una pantalla específica de tu app.
Hasta ahora, usamos una acción simulada proporcionada por el clickable()
sin argumentos para el chip y cada uno de los botones. Esto está bien para las vistas previas, que no son interactivas, pero veamos cómo agregar acciones para los elementos.
LaunchAction
Se puede usar LaunchAction
para iniciar una actividad. Modifiquemos Layout
para que, cuando se presione el botón "Nuevo", se inicie el recorrido del usuario de "nueva conversación".
Busca la línea "TODO: Launch new conversation activity" y reemplaza clickable()
por:
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
clickable(
id = "new_button",
action =
launchAction(
ComponentName(
"com.example.wear.tiles",
"com.example.wear.tiles.messaging.MainActivity",
),
mapOf(
MainActivity.EXTRA_JOURNEY to
ActionBuilders.stringExtra(
MainActivity.EXTRA_JOURNEY_NEW
)
),
),
)
Vuelve a implementar la tarjeta. Ahora, en lugar de no hacer nada, si presionas "Nuevo", se iniciará MainActivity
y comenzará el recorrido del usuario de "nueva conversación":
Del mismo modo, modifica Layout
para que presionar un botón de contacto inicie una conversación con un usuario específico.
Busca la línea "Launch open conversation activity" y reemplaza clickable()
por:
start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt
clickable(
id = contact.id.toString(),
action =
launchAction(
ComponentName(
"com.example.wear.tiles",
"com.example.wear.tiles.messaging.MainActivity",
),
mapOf(
MainActivity.EXTRA_JOURNEY to
ActionBuilders.stringExtra(
MainActivity
.EXTRA_JOURNEY_CONVERSATION
),
MainActivity.EXTRA_CONVERSATION_CONTACT to
ActionBuilders.stringExtra(
contact.name
),
),
),
)
Vuelve a implementar la tarjeta. Ahora, en lugar de no hacer nada, si presionas un contacto, se iniciará una conversación con él:
10. Felicitaciones
¡Felicitaciones! Ya aprendiste a crear una tarjeta para Wear OS.
¿Qué sigue?
Para obtener más información, consulta las implementaciones de tarjetas doradas en GitHub, la guía de tarjetas de Wear OS y los lineamientos de diseño.