Compartir vía


Creación de una esfera del reloj

En esta guía se explica cómo implementar un servicio de esferas personalizadas de reloj para Android Wear 1.0. Se proporcionan instrucciones detalladas para crear un servicio de esferas de reloj digital simplificado, seguido de más código para crear una esfera de reloj de estilo analógico.

Información general

En este tutorial, se crea un servicio simplificado de esferas de reloj para ilustrar los aspectos básicos de la creación de una esfera de reloj personalizada para Android Wear 1.0. El servicio de esferas de reloj muestra un reloj digital sencillo en el que aparece la hora actual en horas y minutos:

Captura de pantalla que muestra la cara inicial del reloj digital.

Después de desarrollar y probar esta esfera de reloj digital, se agrega más código para actualizarlo a una esfera de reloj analógica más sofisticada con tres manillas:

Captura de pantalla que muestra la cara final del reloj analógico.

Los servicios de esferas de reloj se agrupan e instalan como parte de una aplicación Wear 1.0. En los ejemplos siguientes, MainActivity no contiene más que el código de la plantilla de la aplicación Wear 1.0 para que el servicio de esferas de reloj se pueda empaquetar e implementar en el reloj inteligente como parte de la aplicación. En efecto, esta aplicación servirá exclusivamente como un vehículo a fin de cargar el servicio de esferas de reloj en el dispositivo Wear 1.0 (o el emulador) para la depuración y las pruebas.

Requisitos

Para implementar un servicio de esferas de reloj, se requiere lo siguiente:

Aunque Android 5.0 es el nivel de API mínimo para implementar un servicio de esferas de reloj, se recomienda tener Android 5.1 o una versión posterior. Los dispositivos Android Wear que ejecutan Android 5.1 (API 22) o superior permiten que las aplicaciones Wear controlen lo que se muestra en la pantalla mientras el dispositivo está en modo ambiente de bajo consumo. Cuando el dispositivo deja el modo ambiente de bajo consumo, está en modo interactivo. Para obtener más información sobre estos modos, consulta Mantener visible la aplicación.

Inicio de un proyecto de aplicación

Crea un proyecto de Android Wear 1.0 denominado WatchFace (para obtener más información sobre cómo crear proyectos de Xamarin.Android, consulta Hola, Android):

Establece el nombre del paquete en com.xamarin.watchface:

Además, desplázate hacia abajo y habilita los permisos INTERNET y WAKE_LOCK:

Permisos necesarios

A continuación, descarga preview.png, que se agregará a la carpeta drawables más adelante en este tutorial.

Incorporación del paquete Xamarin.Android Wear

Inicia el Administrador de paquetes NuGet (en Visual Studio, haz clic con el botón derecho en Referencias en el Explorador de soluciones y selecciona Administrar paquetes NuGet...). Actualiza el proyecto a la versión estable más reciente de Xamarin.Android.Wear:

Adición del administrador de paquetes NuGet

A continuación, si está instalado Xamarin.Android.Support.v13, desinstálalo:

Eliminación del Administrador de paquetes NuGet

Compila y ejecuta la aplicación en un dispositivo o emulador Wear (para obtener más información sobre cómo hacerlo, consulta la guía de Introducción). Deberías ver la siguiente pantalla de la aplicación en el dispositivo Wear:

Captura de pantalla de la aplicación

En este momento, la aplicación básica Wear no tiene la función de esferas de reloj porque aún no proporciona una implementación de servicio de esferas de reloj. Este servicio se agregará a continuación.

CanvasWatchFaceService

Android Wear implementa esferas de reloj mediante la clase CanvasWatchFaceService. CanvasWatchFaceService se deriva de WatchFaceService, que se deriva de WallpaperService, como se muestra en el diagrama siguiente:

Diagrama de herencia

CanvasWatchFaceService incluye un elemento CanvasWatchFaceService.Engine anidado; crea una instancia de un objeto CanvasWatchFaceService.Engine que realiza el trabajo de dibujar la esfera del reloj. CanvasWatchFaceService.Engine se deriva de WallpaperService.Engine, tal como se muestra en el diagrama anterior.

Un objeto Canvas que CanvasWatchFaceService usa para dibujar la cara del reloj no se muestra en este diagrama: este objeto Canvas se pasa mediante el método OnDraw, como se describe a continuación.

En las secciones siguientes, se creará un servicio de esferas de reloj personalizadas siguiendo estos pasos:

  1. Define una clase denominada MyWatchFaceService que se derive de CanvasWatchFaceService.

  2. En MyWatchFaceService, crea una clase anidada denominada MyWatchFaceEngine que se derive de CanvasWatchFaceService.Engine.

  3. En MyWatchFaceService, implemente un método CreateEngine que cree una instancia de MyWatchFaceEngine y la devuelva.

  4. En MyWatchFaceEngine, implemente el método OnCreate para crear el estilo de la esfera de reloj y realizar cualquier otra tarea de inicialización.

  5. Implementa el método OnDraw de MyWatchFaceEngine. Se llama a este método cada vez que es necesario volver a dibujar la esfera de reloj (es decir, está invalidada). OnDraw es el método que dibuja (y vuelve a dibujar) elementos de la esfera de reloj, como las manillas de la hora, minutos y segundos.

  6. Implementa el método OnTimeTick de MyWatchFaceEngine. Se llama a OnTimeTick al menos una vez por minuto (en los modos ambiente e interactivo) o cuando ha cambiado la fecha y la hora.

Para obtener más información sobre CanvasWatchFaceService, consulta la documentación de la API CanvasWatchFaceService de Android. Del mismo modo, CanvasWatchFaceService.Engine explica la implementación real de la esfera de reloj.

Incorporación de CanvasWatchFaceService

Agrega un nuevo archivo denominado MyWatchFaceService.cs (en Visual Studio, haz clic con el botón derecho en WatchFace en el Explorador de soluciones, haz clic en Agregar > Nuevo elemento... y selecciona Clase).

Reemplaza el contenido de este archivo por el código siguiente:

using System;
using Android.Views;
using Android.Support.Wearable.Watchface;
using Android.Service.Wallpaper;
using Android.Graphics;

namespace WatchFace
{
    class MyWatchFaceService : CanvasWatchFaceService
    {
        public override WallpaperService.Engine OnCreateEngine()
        {
            return new MyWatchFaceEngine(this);
        }

        public class MyWatchFaceEngine : CanvasWatchFaceService.Engine
        {
            CanvasWatchFaceService owner;
            public MyWatchFaceEngine (CanvasWatchFaceService owner) : base(owner)
            {
                this.owner = owner;
            }
        }
    }
}

MyWatchFaceService (derivado de CanvasWatchFaceService) es el "programa principal" de la esfera de reloj. MyWatchFaceService implementa solo un método, OnCreateEngine, que crea instancias y devuelve un objeto MyWatchFaceEngine (MyWatchFaceEngine se deriva de CanvasWatchFaceService.Engine). El objeto MyWatchFaceEngine del que se ha creado una instancia debe devolverse como WallpaperService.Engine. El objeto de encapsulación MyWatchFaceService se pasa al constructor.

MyWatchFaceEngine es la implementación real de la esfera de reloj: contiene el código que dibuja la esfera de reloj. También controla eventos del sistema, como cambios de pantalla (modos ambiente/interactivo, desactivación de la pantalla, etc.).

Implementación del método OnCreate del motor

El método OnCreate inicializa la esfera de reloj. Agrega el campo siguiente a MyWatchFaceEngine:

Paint hoursPaint;

Este objeto Paint se usará para dibujar la hora actual en la esfera de reloj. Después, agrega el método siguiente a MyWatchFaceEngine:

public override void OnCreate(ISurfaceHolder holder)
{
    base.OnCreate (holder);

    SetWatchFaceStyle (new WatchFaceStyle.Builder(owner)
        .SetCardPeekMode (WatchFaceStyle.PeekModeShort)
        .SetBackgroundVisibility (WatchFaceStyle.BackgroundVisibilityInterruptive)
        .SetShowSystemUiTime (false)
        .Build ());

    hoursPaint = new Paint();
    hoursPaint.Color = Color.White;
    hoursPaint.TextSize = 48f;
}

Se llama a OnCreate poco después de que MyWatchFaceEngine se inicie. Configura WatchFaceStyle (que controla cómo interactúa el dispositivo Wear con el usuario) y crea una instancia del objeto Paint que se usará para mostrar la hora.

La llamada a SetWatchFaceStyle hace lo siguiente:

  1. Establece el modo de ver el código sin salir en PeekModeShort, lo que hace que las notificaciones aparezcan como tarjetas de "ver el código sin salir" pequeñas en la pantalla.

  2. Establece la visibilidad del fondo en Interruptive, lo que hace que el fondo de una tarjeta de ver el código sin salir se muestre brevemente si representa una notificación que interrumpe.

  3. Deshabilita la hora predeterminada de la interfaz de usuario del sistema que se dibuja en la esfera de reloj para que la esfera de reloj personalizada pueda mostrar la hora en su lugar.

Para obtener más información sobre estas y otras opciones de estilo de esfera de reloj, consulta la documentación de la API de Android WatchFaceStyle.Builder.

Después de completarse SetWatchFaceStyle, OnCreate crea una instancia del objeto Paint (hoursPaint) y establece su color en blanco y su tamaño de texto en 48 píxeles (TextSize debe especificarse en píxeles).

Implementación del método OnDraw del motor

El método OnDraw es quizás el método CanvasWatchFaceService.Engine más importante: es el que dibuja elementos de la esfera de reloj, como dígitos y manillas de la esfera de reloj. En el ejemplo siguiente, dibuja una cadena de hora en la esfera de reloj. Agregue el método siguiente a MyWatchFaceEngine:

public override void OnDraw (Canvas canvas, Rect frame)
{
    var str = DateTime.Now.ToString ("h:mm tt");
    canvas.DrawText (str,
        (float)(frame.Left + 70),
        (float)(frame.Top  + 80), hoursPaint);
}

Cuando Android llama a OnDraw, pasa una instancia de Canvas y los límites en los que se puede dibujar la esfera. En el ejemplo de código anterior, DateTime se usa para calcular la hora actual en horas y minutos (en formato de 12 horas). La cadena de hora resultante se dibuja en el lienzo mediante el método Canvas.DrawText. La cadena aparecerá 70 píxeles por encima del borde izquierdo y 80 píxeles por debajo del borde superior.

Para obtener más información sobre el método OnDraw, consulta la documentación de la API OnDraw de Android.

Implementación del método OnTimeTick del motor

Android llama periódicamente al método OnTimeTick para actualizar la hora que muestra la esfera de reloj. Se le llama al menos una vez por minuto (en los modos ambiente e interactivo) o cuando ha cambiado la fecha y la hora o la zona horaria. Agregue el método siguiente a MyWatchFaceEngine:

public override void OnTimeTick()
{
    Invalidate();
}

Esta implementación de OnTimeTick simplemente llama a Invalidate. El método Invalidate programa OnDraw para volver a dibujar la esfera de reloj.

Para obtener más información sobre el método OnTimeTick, consulta la documentación de la API OnTimeTick de Android.

Registro de CanvasWatchFaceService

MyWatchFaceService debe registrarse en el archivo AndroidManifest.xml de la aplicación Wear asociada. Para ello, agrega el siguiente XML a la sección <application>:

<service
    android:name="watchface.MyWatchFaceService"
    android:label="Xamarin Sample"
    android:allowEmbedded="true"
    android:taskAffinity=""
    android:permission="android.permission.BIND_WALLPAPER">
    <meta-data
        android:name="android.service.wallpaper"
        android:resource="@xml/watch_face" />
    <meta-data
        android:name="com.google.android.wearable.watchface.preview"
        android:resource="@drawable/preview" />
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
        <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
    </intent-filter>
</service>

El XML hace lo siguiente:

  1. Establece el permiso android.permission.BIND_WALLPAPER. Este permiso concede al servicio de esferas de reloj permiso para cambiar el fondo de pantalla del sistema en el dispositivo. Ten en cuenta que este permiso debe establecerse en la sección <service> en lugar de en la sección <application> externa.

  2. Define un recurso watch_face. Este recurso es un archivo XML corto que declara un recurso wallpaper (este archivo se creará en la sección siguiente).

  3. Declara una imagen que se puede dibujar denominada preview, que la mostrará la pantalla de selección del selector del reloj.

  4. Incluye un elemento intent-filter para informar a Android de que MyWatchFaceService mostrará una esfera de reloj.

Esto completa el código del ejemplo básico WatchFace. El siguiente paso es agregar los recursos necesarios.

Incorporación de archivos de recursos

Para poder ejecutar el servicio de reloj, debes agregar el recurso watch_face y la imagen de vista previa. En primer lugar, crea un archivo XML en Resources/xml/watch_face.xml y reemplaza su contenido por el siguiente XML:

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />

Establece la acción de compilación de este archivo en AndroidResource:

Este archivo de recursos define un elemento wallpaper sencillo que se usará para la esfera de reloj.

Si aún no lo has hecho, descarga preview.png. Instálalo en Resources/drawable/preview.png. Asegúrate de agregar este archivo al proyecto WatchFace. En el selector de esferas de reloj del dispositivo Wear se muestra al usuario esta imagen de vista previa. A fin de crear una imagen de vista previa para su propia esfera de reloj, puedes tomar una captura de pantalla de la esfera de reloj mientras se ejecuta. (Para obtener más información sobre cómo obtener capturas de pantalla de dispositivos Wear, consulta Tomar capturas de pantalla).

¡Pruébelo!

Compila e implementa la aplicación en el dispositivo Wear. Deberías ver que la pantalla de la aplicación Wear aparece como antes. Haz lo siguiente para habilitar la nueva esfera de reloj:

  1. Desliza el dedo a la derecha hasta que veas el fondo de la pantalla del reloj.

  2. Toca y mantén pulsado en cualquier parte del fondo de la pantalla durante dos segundos.

  3. Desliza el dedo de izquierda a derecha para navegar por las distintas esferas de reloj.

  4. Selecciona la esfera de reloj Ejemplo de Xamarin (que se muestra a la derecha):

    Selector de la cara de reloj

  5. Pulse la esfera de reloj Ejemplo de Xamarin para seleccionarla.

Esto cambia la esfera de reloj del dispositivo Wear para usar el servicio de esferas de reloj personalizadas implementado hasta ahora:

Captura de pantalla que muestra un reloj digital personalizado que se ejecuta en el dispositivo Wear.

Se trata de una esfera de reloj relativamente rudimentaria porque la implementación de la aplicación es mínima (por ejemplo, no incluye un fondo de la esfera de reloj y no llama a métodos Paint de suavizado de contorno para mejorar la apariencia). Pero implementa la función básica necesaria para crear una esfera de reloj personalizada.

En la sección siguiente, esta esfera de reloj se actualizará a una implementación más sofisticada.

Actualización de la esfera de reloj

En el resto de este tutorial, MyWatchFaceService se actualiza para mostrar una esfera de reloj de estilo analógico y se amplía para admitir más características. Se agregarán las siguientes capacidades para crear la esfera de reloj actualizada:

  1. Indica la hora con las manillas analógicas de la hora, los minutos y los segundos.

  2. Reacciona a los cambios en la visibilidad.

  3. Responde a los cambios entre el modo ambiente y el modo interactivo.

  4. Lee las propiedades del dispositivo Wear subyacente.

  5. Actualiza automáticamente la hora en que se produce un cambio de zona horaria.

Antes de implementar los cambios de código siguientes, descarga drawable.zip, descomprímelo y mueve los archivos .png descomprimidos a Resources/Drawable (sobrescribe el preview.png anterior). Agrega los nuevos archivos .png al proyecto WatchFace.

Actualización de características del motor

El siguiente paso es actualizar MyWatchFaceService.cs a una implementación que dibuja una esfera de reloj analógico y admite nuevas características. Reemplaza el contenido de MyWatchFaceService.cs por la versión analógica del código de la esfera de reloj en MyWatchFaceService.cs (puedes cortar y pegar este origen en el archivo MyWatchFaceService.cs existente).

Esta versión de MyWatchFaceService.cs agrega más código a los métodos existentes e incluye métodos invalidados adicionales para agregar más función. En las secciones siguientes se proporciona un recorrido guiado del código fuente.

OnCreate

El método OnCreate actualizado configura el estilo de la esfera de reloj como antes, pero incluye algunos pasos adicionales:

  1. Establece la imagen de fondo en el recurso xamarin_background que reside en Resources/drawable-hdpi/xamarin_background.png.

  2. Inicializa objetos Paint para dibujar la manilla de la hora, la de los minutos y la de los segundos.

  3. Inicializa un objeto Paint para dibujar los tics de la hora alrededor del borde de la esfera de reloj.

  4. Crea un temporizador que llama al método Invalidate (volver a dibujar) para que la manilla de los segundos se vuelva a dibujar cada segundo. Ten en cuenta que este temporizador es necesario porque OnTimeTick llama a Invalidate solo una vez cada minuto.

En este ejemplo solo se incluye una imagen de xamarin_background.png, pero es posible que quieras crear una imagen de fondo diferente para cada densidad de pantalla que admitirá la esfera de reloj personalizada.

OnDraw

El método OnDraw actualizado dibuja una esfera de reloj de estilo analógico mediante los pasos siguientes:

  1. Obtiene la hora actual, que ahora se mantiene en un objeto time.

  2. Determina los límites de la superficie del dibujo y su centro.

  3. Dibuja el fondo, escalado para ajustarse al dispositivo cuando se dibuja el fondo.

  4. Dibuja doce tics alrededor de la esfera de reloj (correspondiente a las horas de la esfera de reloj).

  5. Calcula el ángulo, la rotación y la longitud de cada manilla del reloj.

  6. Dibuja cada manilla en la superficie del reloj. Ten en cuenta que la segunda manilla no se dibuja si el reloj está en modo ambiente.

OnPropertiesChanged

Se llama a este método para informar a MyWatchFaceEngine sobre las propiedades del dispositivo Wear (como el modo ambiente de pocos bits y la protección contra la persistencia en pantalla). En MyWatchFaceEngine, este método solo comprueba el modo ambiente de pocos bits (en modo ambiente de pocos bits, la pantalla admite menos bits para cada color).

Para obtener más información sobre este método, consulta la documentación de la API OnPropertiesChanged de Android.

OnAmbientModeChanged

Se llama a este método cuando el dispositivo Wear activa o desactiva el modo ambiente. En la implementación de MyWatchFaceEngine, la esfera de reloj deshabilita el suavizado de contorno cuando está en modo ambiente.

Para obtener más información sobre este método, consulta la documentación de la API OnAmbientModeChanged de Android.

OnVisibilityChanged

Se llama a este método cada vez que el reloj se vuelve visible u oculto. En MyWatchFaceEngine, este método registra o anula el registro del receptor de zona horaria (descrito a continuación) según el estado de visibilidad.

Para obtener más información sobre este método, consulta la documentación de la API OnVisibilityChanged de Android.

Característica de zona horaria

El nuevo MyWatchFaceService.cs también incluye función para actualizar la hora actual cada vez que cambia la zona horaria (por ejemplo, mientras viaja por varias zonas horarias). Cerca del final de MyWatchFaceService.cs, se define un cambio de zona horaria BroadcastReceiver que controla los objetos Intent modificados por la zona horaria:

public class TimeZoneReceiver: BroadcastReceiver
{
    public Action<Intent> Receive { get; set; }
    public override void OnReceive (Context context, Intent intent)
    {
        if (Receive != null)
            Receive (intent);
    }
}

El método OnVisibilityChanged llama automáticamente a los métodos RegisterTimezoneReceiver y UnregisterTimezoneReceiver. Se llama a UnregisterTimezoneReceiver cuando se cambia el estado de visibilidad de la esfera de reloj a oculto. Cuando la esfera de reloj vuelve a estar visible, se llama a RegisterTimezoneReceiver (ver el método OnVisibilityChanged).

El método RegisterTimezoneReceiver del motor declara un controlador para el evento Receive de este receptor de zona horaria; este controlador actualiza el objeto time con la nueva hora cada vez que se cruza una zona horaria:

timeZoneReceiver = new TimeZoneReceiver ();
timeZoneReceiver.Receive = (intent) => {
    time.Clear (intent.GetStringExtra ("time-zone"));
    time.SetToNow ();
};

Se crea y se registra un filtro de intención para el receptor de zona horaria:

IntentFilter filter = new IntentFilter(Intent.ActionTimezoneChanged);
Application.Context.RegisterReceiver (timeZoneReceiver, filter);

El método UnregisterTimezoneReceiver anula el registro del receptor de zona horaria:

Application.Context.UnregisterReceiver (timeZoneReceiver);

Ejecución de la esfera de reloj mejorada

Compila e implementa de nuevo la aplicación en el dispositivo Wear. Selecciona la esfera de reloj del selector de esferas de reloj tal como se ha hecho anteriormente. La vista previa del selector de reloj se muestra a la izquierda y la nueva esfera de reloj se muestra a la derecha:

Captura de pantalla que muestra una cara analógica mejorada en el selector y en el dispositivo.

En esta captura de pantalla, la manilla de los segundos se mueve una vez por segundo. Al ejecutar este código en un dispositivo Wear, la manilla de los segundos desaparece cuando el reloj entra en modo ambiente.

Resumen

En este tutorial, se ha implementado y probado una esfera de reloj personalizada de Android Wear 1.0. Se han introducido las clases CanvasWatchFaceService y CanvasWatchFaceService.Engine, y se han implementado los métodos esenciales de la clase de motor para crear una esfera de reloj digital sencilla. Esta implementación se ha actualizado con más función para crear una esfera de reloj analógica y se han implementado métodos adicionales para controlar los cambios en la visibilidad, el modo ambiente y las diferencias en las propiedades del dispositivo. Por último, se ha implementado un receptor de difusión de zona horaria para que el reloj actualice automáticamente la hora en que se cruza una zona horaria.