Xamarin.Froms でパズルゲームを作る(iOS/Android版)

de:code の直前に発表になった Xamarin.Forms ですが、拙著のサンプルも、それぞれビューを作っているわけで。

日経BP書店|C#によるiOS、Android、Windowsアプリケーション開発入門
http://ec.nikkeibp.co.jp/item/books/P98340.html

そんな訳で、サンプルコードの TMPuzzle を Xamarin,Forms にコンバートしています。サンプル自体は一枚絵(ひとつのビュー)になっているので、比較的コンバートは楽なハズ…なのですが、いくつかコツが要りそうなでメモ的に残しておきます。

まだ途中の段階ですが、iOS/Android版を Github に公開しておきます。
https://github.com/moonmile/TMPuzzleXForms

■ビューを XAML で作る

https://github.com/moonmile/TMPuzzleXForms/blob/master/TMPuzzleXForms/TMPuzzleXForms/MainPage.xaml

サンプルでは、axml/storyboard/XAML で作ったので、今回も XAML で作ります。Xamarin Studio ではデザイナは動かないのですがコード補完ができるのでだいたいの動きが想像できます。デモのサンプル https://github.com/xamarin/xamarin-forms-samples にある ButtonXaml が XAML を使ったコードです。*.xaml と *.xaml.cs の2つがあるので、コードビハイドが書けます。MVVM スタイルにするときはバインドを使えばいいのですが、ここではがりがりとコードを書きつけてしまいます。バインド自体は、MS謹製のXAMLと同じように Text=”{Binding …}” の構文で書けるので、ここは便利。使えるコントロールは http://iosapi.xamarin.com/?link=N%3aXamarin.Forms を参照すれば OK です。TextBox が Editor になっているとか、いくつか違いがありますが、このあたりは iOS/Android/Windows の混合になるので仕方がないところです。

ビュー自体は、レイアウトが異なるので、http://developer.xamarin.com/guides/cross-platform/xamarin-forms/controls/layouts/ を見てベースを決めます。TMPuzzle は Windows ストア寄りに作ってあるので Grid を使ってみたのですが、iOS だとうまくレイアウトができませんね。スマートフォンのような小さ目の画面であれば? Frame を使って iOS のように位置固定(Windows の Xaml ならば canvas)で良いかもしれません。TMPuzzle 自体はゲームアプリなので、たくさんのコントロールを乗せていますが、普通のコンテンツを表示するならば、ContentViewStackLayout でもいいかもしれません。

image

プロジェクトの構成は、Blank App (Xamarin.Forms. Shared) を使っています。PCL でも良いのですが、サンプル自体はコードビハイドを使っているのと、プラットフォームごとの処理が必要になって時には共有プロジェクト内で #if してしまえばよいので、こっちのほうが融通が利きます。

image

https://github.com/moonmile/TMPuzzleXForms/blob/master/TMPuzzleXForms/TMPuzzleXForms/App.cs 共有プロジェクトの App.cs を書き換えます。MainPage.xaml を共有プロジェクトに追加したので、new MainPage() でページを作成します。コードビハイドの場合は、このあとゴリゴリ内容を書いていますが、ひとまず XAML を書いて Andorid か iOS のシミュレーターで動かすとよいでしょう。

public class App
{
	public static Page GetMainPage()
	{
		return new MainPage();
	}
}

画像リソースも多分、共有プロジェクトにおけるハズなのですが、フォルダが異なる(Android の場合は Resources/Drawable、iOS の場合は Resources)となるので、各プラットフォームのフォルダに置いています。画像リソースは一律、

var img = ImageSource.FromFile("MarkNone.png");

のように取り出せるので、iOS/Android を区別する必要はありません。

ちなみに、Xamarin.Forms とは関係ないですが、ファイル呼び出しは

var file = System.IO.Path.Combine(documents, "mydata.xml");

のように共通化できます(Windows Phone の場合は調査中)。

■MainPage.xaml.cs

https://github.com/moonmile/TMPuzzleXForms/blob/master/TMPuzzleXForms/TMPuzzleXForms/MainPage.xaml.cs

ページを表示するときの初期化は、Android では OnCreate、iOS では ViewDidLoad になるのですが、Xamarin.Forms では(多分)OnAppearing です。 ヘルプを見ると When overridden, allows application developers to customize behavior immediately prior to the Xamarin.Forms.Page becoming visible. とあるので、次回表示するときも呼び出されてしまうような気がするのですが、ここは要調査ですね。TMPuzzle は1枚絵なのでこれを使っています。

各プラットフォームで呼び出す MainActiveity や AppDelegate はそのままです。もともと共通化を意識して作ったサンプルプログラムなので、Xamarin.Forms に移植するとプラットフォーム毎の差がほとんどなくなって、画像ファイルの切り替えぐらいになります。

■画像のクリックイベントはTapGestureRecognizerを使う

Button コントロールには Click イベントがあるのですが、ImageやLabelなどには Click イベントがありません…というか、タップイベント全般がありません。ざっと調べてみると、コントロールに対するユーザーイベントは Click と TextChanged ぐらいしかありません。
うーむ、困った。これで万事休すかな、と思ったのですが TapGestureRecognizer ってのがありました。iOS と同じでコントロールに対するイベントを取ってきます。

TapGestureRecognizer tapGestureRecognizer = new TapGestureRecognizer
{
    TappedCallback = img_Click
};
// 表示用マークにイベントをつける
_marks = new Image[DataModel.BOARD_X_MAX * DataModel.BOARD_Y_MAX];
_tags = new Dictionary<Element, int>();
for (int i = 0; i < DataModel.BOARD_X_MAX * DataModel.BOARD_Y_MAX; i++)
{
    var img = _marks[i] = this.FindByName<Image>(string.Format("mark{0}", i));
    img.GestureRecognizers.Add(tapGestureRecognizer);
    _tags[img] = i;
}

こんな風に、イベント先の img_Click を設定しておいて、img.GestureRecognizers.Add のように追加していきます。GestureRecognizers 自体は、View クラスにあるので大抵のコントロールにはついています。

いわゆる sender が View オブジェクトで渡されるので、これを目的のコントロールにキャストします。引数自体は object 型なのですが、中身は不明。Image をクリックすると null が来ています…が、ひとまず画像やラベルのタップイベントを取得できます。

public async void img_Click(View view, object args)
{
    // 再入禁止
    if (_flag == true) return;
    _flag = true;
    Image mark = view as Image;

■Android と iPhone の動作

同じ XAML ファイルを使ったのですが、iOS のほうがレイアウトが崩れています。というか、Grid だとちょっと辛いものがありそうですね。Xamarin.Forms のサンプルは、flowlayout を使っているので、どれも同じようになりますが、複雑なレイアウトの場合にはそれぞれの調節が必要っぽいです。

image

image

引き続き Windows Phone を作成。

カテゴリー: Android, Xamarin, iOS パーマリンク