ウォッチの文字盤を作成する

このガイドでは、カスタムの Android Wear 1.0 用ウォッチの文字盤サービスを実装する方法について説明します。 必要最小限のデジタル ウォッチの文字盤サービスを構築するための段階的な手順が提供され、その後にアナログ スタイルのウォッチの文字盤を作成するためのコードが追加されます。

概要

このチュートリアルでは、カスタムの Android Wear 1.0 のウォッチの文字盤の作成の要点を説明するために、基本的なウォッチの文字盤サービスを作成します。 最初の文字盤サービスでは、現在時刻を時と分で表示する単純なデジタル時計が表示されます。

最初のデジタル ウォッチ フェイスを示すスクリーンショット。

このデジタル時計の文字盤が開発およびテストされた後、さらにコードが追加され、3 つの針を備えたより洗練されたアナログ時計の文字盤にアップグレードされます。

最後のアナログ ウォッチ フェイスを示すスクリーンショット。

ウォッチの文字盤サービスは、Wear 1.0 アプリの一部としてバンドルされ、インストールされます。 次の例では、MainActivity には Wear 1.0 アプリ テンプレートのコードのみが含まれているため、ウォッチの文字盤サービスをパッケージ化し、アプリの一部としてスマート ウォッチにデプロイできます。 実際、このアプリは純粋に、デバッグとテストのためにウォッチの文字盤サービスを Wear 1.0 デバイス (またはエミュレーター) に読み込むための手段として機能します。

要件

ウォッチの文字盤サービスを実装するには、次のものが必要です。

ウォッチの文字盤サービスを実装するための最小 API レベルは Android 5.0 ですが、Android 5.1 以降をお勧めします。 Android 5.1 (API 22) 以降を実行している Android Wear デバイスでは、デバイスが低電力 "アンビエント" モードにあるときに、Wear アプリで画面に表示される内容を制御できます。 デバイスが低電力 "アンビエント" モードを終了すると、"対話型" モードになります。 これらのモードの詳細については、アプリを表示し続けるに関するページを参照してください。

アプリ プロジェクトを開始する

WatchFace という新しい Android Wear 1.0 プロジェクトを作成します (新しい Xamarin.Android プロジェクトの作成の詳細については、「Hello、Android」を参照してください)。

パッケージ名を com.xamarin.watchface に設定します。

さらに、下にスクロールして、[INTERNET] アクセス許可と [WAKE_LOCK] アクセス許可を有効にします。

必要なアクセス許可

次に、preview.png をダウンロードします。これは、このチュートリアルの後半で drawables フォルダーに追加されます。

Xamarin.Android Wear パッケージを追加する

NuGet パッケージ マネージャーを起動します (Visual Studio で、ソリューション エクスプローラー[参照] を右クリックし、[NuGet パッケージの管理] を選びます)。プロジェクトを Xamarin.Android.Wear の最新の安定バージョンに更新します。

NuGet パッケージ マネージャーの追加

次に、Xamarin.Android.Support.v13 がインストールされている場合は、アンインストールします。

NuGet パッケージ マネージャーの削除

Wear デバイスまたはエミュレーター上でアプリをビルドして実行します (これを行う方法の詳細については、概要に関するガイドを参照してください)。 Wear デバイスに次のアプリ画面が表示されます。

アプリのスクリーンショット

この時点では、基本的な Wear アプリにはウォッチの文字盤サービスの実装がまだ提供されていないため、ウォッチの文字盤機能はありません。 このサービスは次で追加される予定です。

CanvasWatchFaceService

Android Wear は、CanvasWatchFaceService クラスを介してウォッチの文字盤を実装します。 次の図に示すように、CanvasWatchFaceService の派生元は WatchFaceService で、それ自体は WallpaperService から派生します。

継承ダイアグラム

CanvasWatchFaceService には入れ子になった CanvasWatchFaceService.Engine が含まれており、ウォッチの文字盤の描画で実際に作業を行う CanvasWatchFaceService.Engine オブジェクトのインスタンスを作成します。 上の図に示すように、CanvasWatchFaceService.EngineWallpaperService.Engine から派生しています。

この図には、CanvasWatchFaceService がウォッチの文字盤の描画に使う Canvas は示されていません。この Canvas は、以下で説明するように OnDraw メソッドを介して渡されます。

次のセクションでは、以下の手順に従ってカスタムのウォッチの文字盤サービスを作成します。

  1. CanvasWatchFaceService から派生した MyWatchFaceService というクラスを定義します。

  2. MyWatchFaceService 内に、CanvasWatchFaceService.Engine から派生した MyWatchFaceEngine という入れ子になったクラスを作成します。

  3. MyWatchFaceService で、MyWatchFaceEngine のインスタンスを作成してそれを返す CreateEngine メソッドを実装します。

  4. MyWatchFaceEngine で、ウォッチの文字盤のスタイルを作成し、その他の初期化タスクを実行する OnCreate メソッドを実装します。

  5. MyWatchFaceEngineOnDraw メソッドを実装します。 このメソッドは、ウォッチの文字盤を再描画する必要がある (つまり、"無効化" された) 場合に呼び出されます。 OnDraw は、時針、分針、秒針などの時計の文字盤要素を描画 (および再描画) するメソッドです。

  6. MyWatchFaceEngineOnTimeTick メソッドを実装します。 OnTimeTick は、少なくとも 1 分に 1 回 (アンビエント モードと対話型モードの両方で)、または日付/時刻が変更されたときに呼び出されます。

CanvasWatchFaceService の詳細については、Android の CanvasWatchFaceService API ドキュメントを参照してください。 同様に、CanvasWatchFaceService.Engine では、ウォッチの文字盤の実際の実装について説明されています。

CanvasWatchFaceService を追加する

MyWatchFaceService.cs という新しいファイルを追加します (Visual Studio で、ソリューション エクスプローラー[WatchFace] を右クリックし、[追加] > [新しい項目] をクリックして、[クラス] を選びます)。

このファイルの内容を次のコードに置き換えます。

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 (CanvasWatchFaceService から派生) は、ウォッチの文字盤の "メイン プログラム" です。 MyWatchFaceService はメソッドを 1 つだけ実装しており (OnCreateEngine)、MyWatchFaceEngine オブジェクトのインスタンスを作成して返します (MyWatchFaceEngineCanvasWatchFaceService.Engine から派生します)。 インスタンスが作成された MyWatchFaceEngine オブジェクトは、WallpaperService.Engine として返す必要があります。 カプセル化された MyWatchFaceService オブジェクトがコンストラクターに渡されます。

MyWatchFaceEngine は実際のウォッチの文字盤の実装であり、ウォッチの文字盤を描画するコードが含まれています。 また、アンビエント/対話型モード、画面のオフといった、画面の変更などのシステム イベントも処理します。

Engine の OnCreate メソッドを実装する

OnCreate メソッドはウォッチの文字盤を初期化します。 次のフィールドを MyWatchFaceEngine に追加します。

Paint hoursPaint;

この Paint オブジェクトは、ウォッチの文字盤に現在時刻を描画するために使われます。 次に、以下のメソッドを 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;
}

OnCreate は、MyWatchFaceEngine が開始された直後に呼び出されます。 これは、WatchFaceStyle (Wear デバイスがユーザーと対話する方法を制御する) を設定し、時刻の表示に使われる Paint オブジェクトのインスタンスを作成します。

SetWatchFaceStyle への呼び出しでは次のことが行われます。

  1. "クイック表示モード" を PeekModeShort に設定します。これにより、通知がディスプレイ上に小さな "クイック表示" カードとして表示されます。

  2. 背景の可視性を Interruptive に設定します。これにより、クイック表示カードの背景が中断通知を表す場合に短時間だけ表示されます。

  3. 既定のシステム UI 時間がウォッチの文字盤に描画されないようにし、代わりにカスタムのウォッチの文字盤が時間を表示できるようにします。

これらおよびその他のウォッチの文字盤スタイルのオプションの詳細については、Android の WatchFaceStyle.Builder API ドキュメントを参照してください。

SetWatchFaceStyle が完了すると、OnCreatePaint オブジェクト (hoursPaint) のインスタンスを作成し、その色を白、テキスト サイズを 48 ピクセルに設定します (TextSize はピクセル単位で指定する必要があります)。

Engine の OnDraw メソッドを実装する

OnDraw メソッドはおそらく最も重要な CanvasWatchFaceService.Engine のメソッドです。これは、数字や文字盤の針などの文字盤要素を実際に描画するメソッドです。 次の例では、ウォッチの文字盤に時刻の文字列を描画します。 次のメソッドを 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);
}

Android が OnDraw を呼び出すと、Canvas インスタンスと、文字盤を描画できる境界が渡されます。 上記のコード例では、現在時刻を時と分 (12 時間形式) で計算するために DateTime が使われています。 結果の時刻文字列は、Canvas.DrawText メソッドを使ってキャンバス上に描画されます。 文字列は、左端から 70 ピクセル内側に、上端から 80 ピクセル下に表示されます。

OnDraw メソッドの詳細については、Android の onDraw API ドキュメントを参照してください。

Engine の OnTimeTick メソッドを実装する

Android は定期的に OnTimeTick メソッドを呼び出して、ウォッチの文字盤に表示される時刻を更新します。 これは、少なくとも 1 分に 1 回 (アンビエント モードと対話型モードの両方で)、または日付/時刻またはタイムゾーンが変更されたときに呼び出されます。 次のメソッドを MyWatchFaceEngine に追加します。

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

この OnTimeTick の実装は単に Invalidate を呼び出します。 Invalidate メソッドは、OnDraw がウォッチの文字盤を再描画するようにスケジュールします。

OnTimeTick メソッドの詳細については、Android の onTimeTick API ドキュメントを参照してください。

CanvasWatchFaceService を登録する

MyWatchFaceService は、関連付けられた Wear アプリの AndroidManifest.xml に登録する必要があります。 これを行うには、次の XML を <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>

この XML は次のことを行います。

  1. android.permission.BIND_WALLPAPER アクセス許可を設定します。 このアクセス許可により、ウォッチの文字盤サービスにデバイスのシステム壁紙を変更するアクセス許可が与えられます。 このアクセス許可は、外部の <application> セクションではなく、<service> セクションで設定する必要があることに注意してください。

  2. watch_face リソースを定義します。 このリソースは、wallpaper リソースを宣言する短い XML ファイルです (このファイルは次のセクションで作成します)。

  3. ウォッチ ピッカーの選択画面で表示される preview というドローアブル画像を宣言します。

  4. MyWatchFaceService がウォッチの文字盤を表示することを Android に知らせるための intent-filter が含まれています。

これで、基本的な WatchFace サンプルのコードが完成しました。 次の手順では、必要なリソースを追加します。

リソース ファイルの追加

ウォッチ サービスを実行する前に、watch_face リソースとプレビュー画像を追加する必要があります。 まず、Resources/xml/watch_face.xml で新しい XML ファイルを作成し、その内容を次の XML に置き換えます。

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

このファイルのビルド アクションを AndroidResource に設定します。

このリソース ファイルは、ウォッチの文字盤に使われる単純な wallpaper 要素を定義します。

まだダウンロードしていない場合は、preview.png をダウンロードします。 Resources/drawable/preview.png にインストールします。 このファイルを必ず WatchFace プロジェクトに追加します。 このプレビュー画像は、Wear デバイスのウォッチの文字盤ピッカーでユーザーに表示されます。 自分のウォッチの文字盤のプレビュー画像を作成するには、実行中にウォッチの文字盤のスクリーンショットを撮ることができます (Wear デバイスからスクリーンショットを取得する方法の詳細については、「スクリーンショットを撮る」を参照してください)。

試してみる

アプリをビルドして Wear デバイスにデプロイします。 これまでと同じように、Wear アプリ画面が表示されます。 新しいウォッチの文字盤を有効にするには、以下を行います。

  1. ウォッチ画面の背景が表示されるまで右にスワイプします。

  2. 画面の背景の任意の場所を 2 秒間長押しします。

  3. 左から右にスワイプして、さまざまなウォッチの文字盤を参照します。

  4. Xamarin サンプル ウォッチの文字盤 (右側に表示) を選びます。

    ウォッチフェイス ピッカー

  5. Xamarin サンプル ウォッチの文字盤をタップして選びます。

これにより、Wear デバイスのウォッチの文字盤が変更され、これまでに実装されたカスタムのウォッチの文字盤サービスを使うようになります。

Wear デバイスで実行されているカスタム デジタル ウォッチを示すスクリーンショット。

アプリの実装が非常に最小限であるため、これは比較的粗雑なウォッチの文字盤です (たとえば、ウォッチの文字盤の背景が含まれておらず、外観を改善するための Paint アンチエイリアス メソッドを呼び出していません)。 ただし、カスタムのウォッチの文字盤の作成に必要な最小限の機能は実装されています。

次のセクションでは、このウォッチの文字盤をより洗練された実装にアップグレードします。

ウォッチの文字盤のアップグレード

このチュートリアルの残りの部分では、アナログ スタイルの文字盤を表示するように MyWatchFaceService がアップグレードされ、より多くの機能をサポートするように拡張されます。 アップグレードされたウォッチの文字盤を作成するために、次の機能が追加されます。

  1. アナログの時針、分針、秒針で時刻を示します。

  2. 可視性の変化に反応します。

  3. アンビエント モードと対話型モードの間の変化に応答します。

  4. 基となる Wear デバイスのプロパティを読み取ります。

  5. タイム ゾーンの変更が発生すると、時刻が自動的に更新されます。

次のコード変更を実装する前に、drawable.zip をダウンロードして展開し、展開した .png ファイルを Resources/drawable に移動します (これまでの preview.png を上書きします)。 新しい .png ファイルを WatchFace プロジェクトに追加します。

Engine の機能を更新する

次の手順では、アナログ ウォッチの文字盤を描画し、新機能をサポートする実装に MyWatchFaceService.cs をアップグレードします。 MyWatchFaceService.cs の内容を、ウォッチの文字盤コードのアナログ バージョンの MyWatchFaceService.cs に置き換えます (このソースを切り取って既存の MyWatchFaceService.cs に貼り付けることができます)。

このバージョンの MyWatchFaceService.cs には、既存のメソッドにコードが追加されており、機能を追加するためオーバーライドされたメソッドが含まれています。 次のセクションでは、ソース コードのガイド付きツアーを提供します。

OnCreate メソッド

更新された OnCreate メソッドは以前と同様にウォッチの文字盤のスタイルを構成しますが、いくつかの追加手順が含まれています。

  1. 背景画像を、Resources/drawable-hdpi/xamarin_background.png にある xamarin_background リソースに設定します。

  2. 時針、分針、秒針を描画するための Paint オブジェクトを初期化します。

  3. ウォッチの文字盤の周縁に時間目盛りを描画するための Paint オブジェクトを初期化します。

  4. 秒針が毎秒再描画されるように、Invalidate (再描画) メソッドを呼び出すタイマーを作成します。 OnTimeTickInvalidate を呼び出すのは 1 分に 1 回だけであるため、このタイマーが必要であることに注意してください。

この例には、xamarin_background.png イメージが 1 つだけ含まれています。ただし、カスタムのウォッチの文字盤がサポートする画面密度ごとに異なる背景画像を作成することもできます。

OnDraw

更新された OnDraw メソッドは、以下の手順でアナログ スタイルのウォッチの文字盤を描画します。

  1. 現在の時刻を取得します。これは time オブジェクトに保持されています。

  2. 描画表面の境界とその中心を決定します。

  3. 背景を描画するときに、デバイスに合わせて拡大縮小された背景を描画します。

  4. ウォッチの文字盤の周囲に 12 個の "目盛り" を描きます (ウォッチの文字盤の時間に対応します)。

  5. それぞれの時計の針の角度、回転、長さを計算します。

  6. ウォッチの表面にそれぞれの針を描きます。 ウォッチがアンビエント モードの場合、秒針は表示されないことに注意してください。

OnPropertiesChanged

このメソッドは、Wear デバイスのプロパティ (低ビット アンビエント モードや焼き付き保護など) を MyWatchFaceEngine に通知するために呼び出されます。 MyWatchFaceEngine では、このメソッドは低ビット アンビエント モードのみを確認します (低ビット アンビエント モードでは、画面で色ごとにサポートされるビット数が少なくなります)。

このメソッドの詳細については、Android の onPropertiesChanged API ドキュメントを参照してください。

OnAmbientModeChanged

このメソッドは、Wear デバイスがアンビエント モードに入るとき、または終了するときに呼び出されます。 MyWatchFaceEngine の実装では、アンビエント モードの場合、ウォッチの文字盤はアンチエイリアシングを無効にします。

このメソッドの詳細については、Android の onAmbientModeChanged API ドキュメントを参照してください。

OnVisibilityChanged

このメソッドは、ウォッチが表示または非表示になるたびに呼び出されます。 MyWatchFaceEngine では、このメソッドは可視性の状態に応じてタイム ゾーン レシーバー (後述) を登録/登録解除します。

このメソッドの詳細については、Android の onVisibilityChanged API ドキュメントを参照してください。

タイム ゾーン機能

新しい MyWatchFaceService.cs には、タイム ゾーンが変更されるたびに (タイム ゾーンをまたぐ移動中など)、現在時刻を更新する機能も含まれています。 MyWatchFaceService.cs の末尾近くで、タイム ゾーンが変更された Intent オブジェクトを処理するタイム ゾーン変更の BroadcastReceiver が定義されています。

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

RegisterTimezoneReceiver メソッドと UnregisterTimezoneReceiver メソッドは、OnVisibilityChanged メソッドによって呼び出されます。 UnregisterTimezoneReceiver は、ウォッチの文字盤の可視性の状態が非表示に変更されたときに呼び出されます。 ウォッチの文字盤が再び表示されると、RegisterTimezoneReceiver が呼び出されます (OnVisibilityChanged メソッドを参照)。

エンジンの RegisterTimezoneReceiver メソッドは、このタイム ゾーン レシーバーの Receive イベントのハンドラーを宣言します。このハンドラーは、タイム ゾーンを越えるたびに、time オブジェクトを新しい時刻で更新します。

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

インテント フィルターが作成され、タイム ゾーン レシーバーに登録されます。

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

UnregisterTimezoneReceiver メソッドは、タイム ゾーン レシーバーの登録を解除します。

Application.Context.UnregisterReceiver (timeZoneReceiver);

改良されたウォッチの文字盤を実行する

再度アプリをビルドして Wear デバイスにデプロイします。 以前と同様に、ウォッチの文字盤ピッカーからウォッチの文字盤を選びます。 ウォッチ ピッカーのプレビューが左側に表示され、新しいウォッチの文字盤が右側に表示されます。

スクリーンショットは、ピッカーとデバイスで改善されたアナログ フェイスを示しています。

このスクリーンショットでは、秒針が 1 秒に 1 回動いています。 このコードを Wear デバイスで実行した場合、ウォッチがアンビエント モードになると秒針が消えます。

まとめ

このチュートリアルでは、カスタムの Android Wear 1.0 ウォッチの文字盤を実装してテストしました。 CanvasWatchFaceService クラスと CanvasWatchFaceService.Engine クラスを導入し、シンプルなデジタル ウォッチの文字盤を作成するためにエンジン クラスの重要なメソッドを実装しました。 この実装は更新されてアナログ ウォッチの文字盤を作成するための機能が追加され、可視性、アンビエント モード、デバイス プロパティの違いの変更を処理する追加のメソッドが実装されました。 最後に、タイム ゾーンを越えたときに時計が自動的に時刻を更新できるように、タイム ゾーンのブロードキャスト レシーバーが実装されました。