XamarinでのAndroidのウォッチフェイスアプリ開発に便利なライブラリを作成しました!

こんにちはー!ニアです。

今、Xamarinを使ったAndroid Wear用ウォッチフェイスアプリを作っているのですが、

今回はそのアプリ開発に便利なライブラリを作ってきたので、紹介します。

1. Chronoir_net.Chronica.WatchfaceExtensionの概要

Chronoir_net.Chronica.WatchfaceExtension(旧名 : Chronoir_net.Chronoface.Utility)は、XamanrinでのAndroid Wearのウォッチフェイスアプリ開発において、便利な機能を提供します。

主な機能としては、以下のものがあります。

  • タイムゾーンやバッテリー残量の通知など、ブロードキャストされたIntentを受信し、あらかじめ指定したデリゲートやイベントを実行する機能
  • 現在時刻から、アナログ時計用の時針、分針、秒針の先端位置を求められる機能
  • ContextCompat.GetColorメソッドの戻り値(ARGB値を格納した整数)をColor型オブジェクトに変換する機能

今後、様々な機能を追加する予定です。

1.1. ライブラリ概要

ライブラリ名 Chronoir_net.Chronica.WatchfaceExtension
バージョン 1.0.2
作成者 智中ニア(Nia Tomonaka)
リリース日 2016/12/03
最終更新日 2016/01/25
対応プラットフォーム Xamarin.Android
依存関係 なし
言語 英語(en)、日本語(ja)
ライセンス MIT-Licence
GitHub  https://github.com/Nia-TN1012/Chronoir_net.Chronica.WatchfaceTemplates
プログラミング言語 / フレームワーク C# / Xamarin.Android
開発環境 Visual Studio Community 2015 Update 3

ライブラリはNuGetパッケージとして、NuGet Galleryで公開しています。NuGetパッケージマネージャーなどでダウンロードしてお使いください。

https://www.nuget.org/packages/Chronoir_net.Chronica.WatchfaceExtension/

※ライブラリのダウンロードページは後日追加します。

2. 使い方

※現在、作成中です。

2.1. WatchfaceUtilityクラス

WatchfaceUtilityクラスは、ContextCompat.GetColorメソッドの戻り値(ARGB値を格納した整数)をColor型オブジェクトに変換(ConvertARGBToColorメソッド)したり、AndroidのTimeクラスやJavaのCalendarクラスのオブジェクトを同日時の.NETのDateTime構造体に変換(ConvertToDateTimeメソッド)したりする機能を搭載しています。

// リソースファイルから背景色のリソースを読み込みます。
var backgroundPaint = new Paint();
backgroundPaint.Color = WatchfaceUtility.ConvertARGBToColor(
	ContextCompat.GetColor( owner, Resource.Color.background )
);


// 現在時刻をログに出力します。
Time time;
Java.Util.Calendar cal;
DateTime dtime;

time = new Time();
time.SetToNow();
cal = Java.Util.Calendar.GetInstance( Java.Util.TimeZone.Default );
dtime = DateTime.Now;

#if DEBUG
if( Log.IsLoggable( "Sample", LogPriority.Info ) ) {
	// ConvertToDateTimeメソッドは、AndroidのTime型、JavaのCalendar型、.NETのDateTime型に対応しています。
	Log.Info( "Sample", $"Now time = {WatchfaceUtility.ConvertToDateTime( time ):yyyy/MM/dd HH:mm:ss K}" );
	Log.Info( "Sample", $"Now time = {WatchfaceUtility.ConvertToDateTime( cal ):yyyy/MM/dd HH:mm:ss K}" );
	Log.Info( "Sample", $"Now time = {WatchfaceUtility.ConvertToDateTime( dtime ):yyyy/MM/dd HH:mm:ss K}" );
}
#endif

2.2. BroadcastRecieverクラスの拡張

本ライブラリは、作成したBroadcastRecieverオブジェクトをIntentフィルターとともにApplication.Contextに登録・削除する機能を搭載した、RegistrationSwitchableBroadcastReceiverクラスを提供しています。


//
// 概要:
//     Android.App.Application.Contextへの登録および登録解除する機能を搭載した、Android.Content.BroadcastReceiverクラスを提供します。
public abstract class RegistrationSwitchableBroadcastReceiver : BroadcastReceiver {
	//
	// 概要:
	//     指定したAndroid.Content.IntentのMIMEタイプからChronoir_net.Chronoface.Utility.RegistrationSwitchableBroadcastReceiverクラスの新しいインスタンスを生成します。
	//
	// パラメーター:
	//   filter:
	//     Android.Content.Intentの情報を識別するMIMEタイプ
	public RegistrationSwitchableBroadcastReceiver( string filter );

	//
	// 概要:
	//     Android.Content.BroadcastReceiverがAndroid.App.Application.Contextに登録されているかどうかを表す値を取得・設定します。
	public bool IsRegistered { get; set; }
}

IsRegisteredプロパティにtrueをセットすると、レシーバーをApplication.Contextに登録し、falseをセットすると、レシーバーをApplication.Contextから削除します。

private bool isRegistered = false;
public bool IsRegistered {
	get { return isRegistered; }
	set {
		if( value != isRegistered ) {
			if( value ) {
				Application.Context.RegisterReceiver( this, intentFilter );
			}
			else {
				Application.Context.UnregisterReceiver( this );
			}
			isRegistered = value;
		}
	}
}

バージョン1.0.1より、レシーバーをApplication.Contextに登録するRegisterToContextメソッドと、レシーバーをApplication.Contextから削除するUnregisterFromContextメソッドが追加されました。

Nia12.png
RegistrationSwitchableBroadcastReceiverクラスは抽象クラスなので、継承して使ってね
class DerivedRegistrationSwitchableBroadcastReceiver : RegistrationSwitchableBroadcastReceiver {
	// このメソッドはBroadcastReceiverクラスのメンバーです。
	public override void OnReceive( Context context, Intent intent ) {
		// Intentを受信した時の処理を入れます。
	}
}

また、本ライブラリは、ブロードキャストされたIntentを受信した時、あらかじめ登録しておいた処理を実行可能な、ActionExecutableBroadcastReceiverクラス及びEventExecutableBroadcastReceiverクラスを提供しています。

//
// 概要:
//     ブロードキャストされたAndroid.Content.Intentを受信した時、指定したデリゲートを実行するAndroid.Content.BroadcastReceiverクラスを提供します。
public class ActionExecutableBroadcastReceiver : RegistrationSwitchableBroadcastReceiver {
	//
	// 概要:
	//     ブロードキャストされたAndroid.Content.Intentを受信した時に実行するデリゲートを表します。
	private Action<Intent> broadcastedIntentRecieved;
	
	//
	// 概要:
	//     指定したデリゲートとAndroid.Content.IntentのMIMEタイプからChronoir_net.Chronoface.Utility.ActionExecutableBroadcastReceiverクラスの新しいインスタンスを生成します。
	//
	// パラメーター:
	//   action:
	//     ブロードキャストされたAndroid.Content.Intentを受信した時に実行するデリゲート
	//
	//   filter:
	//     Android.Content.Intentの情報を識別するMIMEタイプ
	public ActionExecutableBroadcastReceiver( Action<Intent> action, string filter );

	//
	// 概要:
	//     ブロードキャストされたAndroid.Content.Intentを受信した時に実行します。
	//
	// パラメーター:
	//   context:
	//     Android.Contextオブジェクト
	//
	//   intent:
	//     ブロードキャストされたAndroid.Content.Intentオブジェクト
	public override void OnReceive( Context context, Intent intent );
}
//
// 概要:
//     ブロードキャストされたAndroid.Content.Intentを受信した時、指定したデリゲートを実行するAndroid.Content.BroadcastReceiverクラスを提供します。
public class EventExecutableBroadcastReceiver : RegistrationSwitchableBroadcastReceiver {
	//
	// 概要:
	//     指定したAndroid.Content.IntentのMIMEタイプからChronoir_net.Chronoface.Utility.EventExecutableBroadcastReceiverクラスの新しいインスタンスを生成します。
	//
	// パラメーター:
	//   filter:
	//     Android.Content.Intentの情報を識別するMIMEタイプ
	public EventExecutableBroadcastReceiver( string filter );

	//
	// 概要:
	//     ブロードキャストされたAndroid.Content.Intentを受信した時に実行するデリゲートを紐付けるイベントハンドラーを表します。
	public event EventHandler<Intent> BroadcastedIntentRecieved;

	//
	// 概要:
	//     ブロードキャストされたAndroid.Content.Intentを受信した時に実行します。
	//
	// パラメーター:
	//   context:
	//     Android.Contextオブジェクト
	//
	//   intent:
	//     ブロードキャストされたAndroid.Content.Intentオブジェクト
	public override void OnReceive( Context context, Intent intent );
}

どちらのクラスも、前述のRegistrationSwitchableBroadcastReceiverクラスを継承しており、RegisterToContextメソッドやUnregisterFromContextメソッドの呼び出し、IsRegisteredプロパティへのセットで、レシーバーの登録・解除ができます。

また、OnRecieveメソッドがオーバーライドされており、ActionExecutableBroadcastReceiverクラスでは、コンストラクターで指定したAction<Intent>型デリゲートに紐付けたメソッドを、EventExecutableBroadcastReceiverクラスでは、EventHandler<Intent>型イベントハンドラー(BroadcastedIntentRecieved)に紐付けたメソッドを実行します。

これらを利用することで、Broadcastrecieverを使った処理を簡潔に表すことができます。

// 定義側
private Action<Intent> broadcastedIntentRecieved;

public override void OnReceive( Context context, Intent intent ) {
	broadcastedIntentRecieved?.Invoke( intent );
}


// 利用側
int count = 0;

var reciever = new ActionExecutableBroadcastReceiver(
	// OnRecieveメソッドで処理したいものをここに記述します。
	intent => {
		count++;
	},
	Intent.ActionTimeTick
);

reciever.IsRegistered = true;
// 定義側
public event EventHandler<Intent> BroadcastedIntentRecieved;

public override void OnReceive( Context context, Intent intent ) {
	BroadcastedIntentRecieved?.Invoke( context, intent );
}


// 利用側
int count = 0;
bool isEven = true;

var reciever = new EventExecutableBroadcastReceiver( Intent.ActionTimeTick );
// OnRecieveメソッドで処理したいものをここに記述します。
reciever.BroadcastedIntentRecieved += ( context, intent ) => {
	count++;
};
// イベントハンドラーなら、コンストラクターで初期化した後でも、自由にイベントを追加・削除したり、
// 複数のイベントをチェーン状につなぐこともできます。
reciever.BroadcastedIntentRecieved += ( context, intent ) => {
	isEven = count % 2 == 0;
};

reciever.IsRegistered = true;

2つのクラスの違いを表に示します。

  ActionExecutableBroadcastReceiver EventExecutableBroadcastReceiver
デリゲートの型 Action<Intent> EventHandler<Intent>
メソッドを指定するタイミング コンストラクターで初期化する時 コンストラクターで初期化後、インスタンスが有効である限り、いつでも
引数
  • Intent型
  • object型(Application.Contextが格納されています。)
  • Intent型
メソッドの数 0~1 0~(メモリが許す限り)
メソッドの追加・削除・変更 不可

◆ Attention

BroadcastRecieverのOnRecieveメソッドは、UIと同じスレッドで実行しており、素早く処理を終えるか別スレッドで実行することが推奨されています

ActionExecutableBroadcastReceiverクラスのデリゲートやEventExecutableBroadcastReceiverのイベントハンドラーにセットする処理も同様に、素早く終える必要があります。

もし、重い処理を実行したい場合、async / awaitキーワードなどを利用して、UIとは別のスレッドで実行するようにします。

int count = 0;

var reciever = new ActionExecutableBroadcastReceiver(
	async intent => {
		count++;
		await Task.Run( () => {
			// 重い処理
			Thread.Sleep( count * 1000 );
		});
	},
	Intent.ActionTimeTick
);

int count = 0;

var reciever = new EventExecutableBroadcastReceiver( Intent.ActionTimeTick );
reciever.BroadcastedIntentRecieved += ( context, intent ) => {
	count++;
};
reciever.BroadcastedIntentRecieved += async ( context, intent ) => {
	await Task.Run( () => {
		// 重い処理
		Thread.Sleep( count * 1000 );
	});
};
Nia03.png
こういう時、C#のasync / awaitが強みになるね

3. おわりに

今回開発したライブラリとともに、ウォッチフェイスアプリをすぐに開発できるプロジェクトテンプレートも作っています。

(実はというと、ソース自体はほぼ完成していて、あとはVSIXパッケージの作成とテスト中です)

完成を楽しみに~

それでは、See you next!

更新履歴

  • 2017/01/26 : ウォッチフェイスアプリ開発用ツールキットのブランド名の決定に伴い、ライブラリ名を変更しました。
この記事をシェアする
Chronoir.netのRSSフィードを購読する

ニア(Nia)

ゲーム系の開発&運用エンジニア(目指すはフルスタック)。主にC#(Unity)/PHPを使っています。最近はDockerやKubernetes、プライベートではAndroid Wearアプリやモバイルアプリ開発(Xamarin)を探求中。好物は紅茶とコーヒー、シラス丼、趣味は写真撮影と音ゲーです

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください