昨日の続きを少し書き足しておきます。
Visual Studio Community 2015 で Xamarin.Forms を使う | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7873
Xamarin.Android/iOS を使ってそれぞれのプラットフォームに向けて C# で書けるのはそれはそれで充分です。UI にしても最新機種にあわせる場合は、Java/Swift で書くだろうし、iOS のほうはタイムラグ無しで Xamarin.iOS が出ていたし、Android のほうも数週間ほどで追随してきます。おそらく、Xamarin が Microsoft に買収された今後も同じ方針でしょう。同じ方針だけど、プラットフォーム三社の思惑があるし、そこはどう動くかわからない。ただし、あまり「最新機能」にとらわれず、Android/iOS かつ UWP の3つのプラットフォームに対して同時にリリースすることを考えると Xamarin.Forms が非常に有効に働きます。v1 の場合は、実質 Android/iOS の二機種だったけど、v2 からは UWP が入ったので Android/iOS/Windows の三機種なのでメリットが大きいのです。
サンプルは XAML がコードで書かれている
Cross-Platform で PCL や Shared をすると、UI のサンプルコードも一緒についてきます。単純に画面に “Welcome to Xamarin Forms!” と表示するだけのものです。PCL(移植可能)なプロジェクトの App.cs に入っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public App() { // The root page of your application MainPage = new ContentPage { Content = new StackLayout { VerticalOptions = LayoutOptions.Center, Children = { new Label { XAlign = TextAlignment.Center, Text = "Welcome to Xamarin Forms!" } } } }; } |
見慣れたような見慣れないような、スタイルで ContentPage を構築します。WPF などでデザイナを使って XAML を書いたり、Android で AXML を使ったり、Xcode で storyboard を使ったりしていると「え?」な気分になるのですが…まあ、え?ってなります。
最初に断っておきますが、モバイル機種のような小さい画面の場合には、結構 XAML のコードベースで作業をするほうが楽だったりします。PC などの液晶モニタに対して画面が小さい(解像度は同じだったりするけど)ので、操作するボタンとかアイコンを細かく配置するよりも、おおざっぱに位置を自動計算させたほうがうまくいきます。また、Android 機種は画面がまちまちなのでそれに揃えるためにもフローレイアウトみたいな感じで作るといいですよね、って感じです。Web のグリッドシステムと同じ感じで作るほうが手早いし、Xamarin.Forms との相性もよいです。
Forms Xaml Page を追加する
これをXAML形式で書けるようにします。
- PCL のプロジェクトに「Forms Xaml Page」を追加します。
PCL プロジェクトに Page1.xaml が追加されます。
- Label の Text プロパティを「Welcome to Xamarin Forms!」に書き換える。
1 2 3 4 5 6 | <? xml version = "1.0" encoding = "utf-8" ?> < ContentPage xmlns = "http://xamarin.com/schemas/2014/forms" xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml" x:Class = "App4.Page1" > < Label Text = "{Binding MainText}" VerticalOptions = "Center" HorizontalOptions = "Center" /> </ ContentPage > |
Binding を使って ViewModel を使うようになっていますが、ここでは Text プロパティを直接変更します。変更はちょっとずつやると解りやすいので。
1 2 3 4 5 6 | <? xml version = "1.0" encoding = "utf-8" ?> < ContentPage xmlns = "http://xamarin.com/schemas/2014/forms" xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml" x:Class = "App4.Page1" > < Label Text = "Welcome to Xamarin Forms and XAML!" VerticalOptions = "Center" HorizontalOptions = "Center" /> </ ContentPage > |
- App.cs のコンストラクタを書き換える。
追加した XAML が表示されるように App コンストラクタを書き換えます。
1 2 3 4 5 | public App() { // The root page of your application this .MainPage = new Page1(); } |
MainPage プロパティに表示したいページクラスを設定すれば ok です。名前が「Page1」になっています。
これをビルドして実行するだけでエミュレータ上での動作が変わります。
試しにボタンのクリックイベントを付けてみる
XAML界隈では嫌われ者のコードビハイドですが、まあ手軽に作るにはこっちのほうが便利です。
XAML を書き換えて、x:Name プロパティにボタンとラベルに名前をつけます。これで、Page1 クラスのプロパティに追加されます。
1 2 3 4 5 6 7 8 9 | <? xml version = "1.0" encoding = "utf-8" ?> < ContentPage xmlns = "http://xamarin.com/schemas/2014/forms" xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml" x:Class = "App4.Page1" > < StackLayout > < Label x:Name = "text1" Text = "Welcome to Xamarin Forms and XAML!" /> < Button x:Name = "btn1" Text = "Click Me" /> </ StackLayout > </ ContentPage > |
コードビハイドを書く。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public partial class Page1 : ContentPage { public Page1() { InitializeComponent(); this .btn1.Clicked += Btn1_Clicked; } private void Btn1_Clicked( object sender, EventArgs e) { this .text1.Text = "クリックした" ; } } |
ボタンの Clicked イベントにラムダ式で書いても良いし、イベント用のメソッドを作ってよいでしょう。
実行すると、こんな感じでボタンが効くようになります。
現時点の Hyper-V のエミュレータのほうは、以下のようなエラーが出て2回目以降のコード変更が反映されません。エミュレータ再起動するか、Android 内で対象のアプリをアンインストールするかの対処が必要です。
1 2 3 4 5 6 7 | 04-06 11:01:12.659 D/OpenGLRenderer( 2235): Use EGL_SWAP_BEHAVIOR_PRESERVED: true 04-06 11:01:12.661 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid) 04-06 11:01:12.662 D/GLHostConnection( 2235): HostConnection:: get () New Host Connection established 0x9c319f40, tid 2235 04-06 11:01:12.687 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid) 04-06 11:01:12.711 D/GLHostConnection( 2235): HostConnection:: get () New Host Connection established 0x9c6e3050, tid 2260 04-06 11:01:12.713 I/OpenGLRenderer( 2235): Initialized EGL, version 1.4 04-06 11:01:12.719 W/EGL_emulation( 2235): eglSurfaceAttrib not implemented |
MVVM化する
このままコードビハイドで書いていっても良いのですが、どうせなので Binding を使って MVVM 化します。Clicked の ICommand はそのままにして、Label のほうだけ。
- ViewModel クラスを追加する。
PCL プロジェクトに ViewModel のクラスを追加します。いろいろ引き継いできた BindableBase を継承します。
ラベルに表示する文字列を Text プロパティで連携させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | using System; using System.ComponentModel; using System.Runtime.CompilerServices; namespace App4 { class MyViewModel : BindableBase { private string _text; public string Text { get { return _text; } set { this .SetProperty( ref this ._text, value); } } } public abstract class BindableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool SetProperty<T>( ref T storage, T value, [CallerMemberName] String propertyName = null ) { if ( object .Equals(storage, value)) return false ; storage = value; this .OnPropertyChanged(propertyName); return true ; } protected void OnPropertyChanged([CallerMemberName] string propertyName = null ) { var eventHandler = this .PropertyChanged; if (eventHandler != null ) { eventHandler( this , new PropertyChangedEventArgs(propertyName)); } } } } |
- XAML で Binding を使う
Label の Text プロパティを Binding に変えます。
1 2 3 4 5 6 7 8 9 | <? xml version = "1.0" encoding = "utf-8" ?> < ContentPage xmlns = "http://xamarin.com/schemas/2014/forms" xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml" x:Class = "App4.Page1" > < StackLayout > < Label Text = "{Binding Text}" /> < Button x:Name = "btn1" Text = "Click Me" /> </ StackLayout > </ ContentPage > |
- ViewModel を BindingContext に割り当てる
いろいろ XAML+MVVM でどのプロパティが使われているのかが混乱していますが、Xamarin.Forms の場合は BindingContext という名前になっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public partial class Page1 : ContentPage { public Page1() { InitializeComponent(); this .btn1.Clicked += Btn1_Clicked; _vm = new MyViewModel(); this .BindingContext = _vm; } MyViewModel _vm; private void Btn1_Clicked( object sender, EventArgs e) { _vm.Text = "MVVM でクリックした" ; } } |
これで無事 MVVM 化ができます。
本家のサンプル
本家のサンプルが github にあるので、ごっそりダウンロードして試してみるとよいです。
GitHub – xamarin/xamarin-forms-samples: Sample apps built using the Xamarin.Forms framework
https://github.com/xamarin/xamarin-forms-samples
これを何処に使うのか?
まあ、一般的な話で言えば Android/iOS のアプリを同時に作れるという話が順当です。が、もうちょっと話を飛躍させると、こんな感じになります。
「FlashAir+Arduino鉄道模型制御-1」 by 綾瀬ヒロ│MakersHub
https://makershub.jp/make/1030
綾瀬ヒロさんも Xamarin を使っていますが、この手のタブレット操作画面を作るのに Xamarin.Forms が有効なのです。スマートフォンじゃなくて、iPad や Surface、大き目の Android タブレット(候補としては Amazon タブレットぐらいしかないのですが)で操作系のアプリを作ろうと思うと、スマートフォンのような小さな画面とは異なった作りが必要になります。かつ、タッチパネル使える/必須なので、操作としてはスマートフォンっぽい感じで使えるのが良い訳です。
UWP が使えるので、Windows IoT Core on Raspberry Pi でも使えますよね(実際使える)。なので、このあたり UWP だけで閉じていれば、Surface, IoT Core 画面でデバッグができる&操作が同じにできるという話なのですが、もうちょっと Android/iPad に範囲を広げると Xamarin.Forms で画面を構成するのは結構有効です。IoT 絡みになれば、スマートフォンの通信や加速度センサーだけでなく、具体的に自作したセンサーの類を使えるわけですから、UI まわりをさっくりと作る(ある意味で、スマートフォンの最新機能には依存しない)ことができます。高めではありますが、Android 組み込みボードのほうでも Xamarin が使えるようになるとよいかなと思ったりしてます。
そのあたりを含めて、入門的に手を付けるのがお薦めです。
Xamarin.Formの資料を探していました。
分かりやすくて、助かりました。