 
Xamarin逆引きTips
Xamarin.Formsでプラットフォームごとの微調整を行うには?
カスタムレンダラーやDependencyServiceの仕組みを使わず、Deviceクラスを利用してプラットフォーム間で異なる部分を微調整する方法を説明する。
Xamarin.Formsでは基本的に、ポータブル・クラス・ライブラリ(PCL)に共通のコードを記述する。そして、プラットフォームごとで違う部分は、カスタムレンダラーやDependencyServiceの仕組みを使用することになる。
 しかし、これらの用法を用いるまでもない、画面の微調整などに関しては、その違いをうまく吸収できるDeviceというクラスが用意されている。今回は、このDeviceクラスのメソッドやプロパティについて解説する。
- *1 なお本Tipsは、Windows上でVisual Studio 2013を使用してXamarin.Forms開発をすることを前提としている(※編集部注: Mac上のXamarin Studioでも同様の手順で、本稿の内容が実現できることは確認している)。使用しているXamarin.Formsのバージョンは、2015年4月23日現在で最新stableの「1.4.2.6355」である。
1. Device.Styles
 Device.Stylesは、6種類の組み込みスタイルクラスとして定義されており、ラベルなどのコントロールのスタイル(=Styleプロパティの値)として利用可能である*2。
- *2 Device.Stylesについては、Xamarin.Forms ver 1.3.2以前では正常に動作しないことが報告されている。
 Device.Stylesクラスの動作を確認するため、(「Tips:「Xamarin.FormsでWebビューを使用するには?」で作成したサンプルの)App.csファイルを以下のように修正した。
| using Xamarin.Forms; namespace WebViewSample {   public class App : Application {     public App() {       MainPage = new MyPage();     }     ……省略……   }   class MyPage : ContentPage {     public MyPage() {       var layout = new StackLayout {         Padding = new Thickness(0,Device.OnPlatform(20,0,0),0,0),       };       layout.Children.Add(new Label {         Text = Device.Styles.BodyStyleKey, Style = Device.Styles.BodyStyle       });       layout.Children.Add(new Label {         Text = Device.Styles.CaptionStyleKey, Style = Device.Styles.CaptionStyle       });       layout.Children.Add(new Label {         Text = Device.Styles.ListItemDetailTextStyleKey, Style = Device.Styles.ListItemDetailTextStyle       });       layout.Children.Add(new Label {         Text = Device.Styles.ListItemTextStyleKey, Style = Device.Styles.ListItemTextStyle       });       layout.Children.Add(new Label {         Text = Device.Styles.SubtitleStyleKey, Style = Device.Styles.SubtitleStyle       });       layout.Children.Add(new Label {         Text = Device.Styles.TitleStyleKey, Style = Device.Styles.TitleStyle       });       Content = layout;     }   } } | 
 6個のLabelコントロールをStackLayoutで縦に並べ、それぞれのText/Styleプロパティに、Device.Stylesクラスに定義されている6種類のフィールド値(キー名/スタイル)を適用している。
このコードを実行すると、次のような画面になる。
 
 
2. Device.GetNamedSize
 フォントのサイズを指定するとき、Device.GetNamedSizeメソッドを使用することができる。
 Device.GetNamedSizeメソッドの動作を確認するため、App.csファイルを以下のように修正した。
| using System; using Xamarin.Forms; namespace WebViewSample {   ……省略……   class MyPage : ContentPage {     public MyPage() {       var layout = new StackLayout{         Padding = new Thickness(20, Device.OnPlatform(20, 0, 0), 0, 0),       };       foreach (NamedSize n in Enum.GetValues(typeof(NamedSize))){ // <-1         var label = new Label();         label.FontSize = Device.GetNamedSize(n, label); // <-2         label.Text = string.Format("{0} ({1})", n, label.FontSize); // <-3         layout.Children.Add(label);       }       Content = layout;     }   } } | 
 NameSize列挙型を一覧し(1)、Device.GetNamedSizeメソッドを使用して、それぞれのフォントサイズを指定した(2)。
なお、表示は、FontSizeの名称と、実際にセットされたフォントサイズである(3)。
このコードを実行すると、次のような画面になる。
 
 
iOSとAndroidを(フォントサイズの数値で)比較したとき、MicroとMediumでは、微妙にサイズが違うのが分かる。また、Defaultの違いから、デバイスごとに、基準となるサイズが違うことが分かる。
3. Device.Idiom
 Device.Idiomは、TargetIdiom列挙型のプロパティであり、動作しているデバイスによって、下記の4種類の値をとる。
- Desktop
- Phone
- Tablet
- Unsupported
 Device.Idiomプロパティの動作を確認するため、App.csファイルを以下のように修正した。
| using Xamarin.Forms; namespace WebViewSample {   ……省略……   class MyPage : ContentPage {     public MyPage() {       Content = new Label { // <-1         XAlign = TextAlignment.Center,         YAlign = TextAlignment.Center,         FontSize = 24,         Text = Device.Idiom.ToString() // <-2       };     }   } } | 
 画面の中央にラベルを配置し(1)、そのテキストにDevice.Idiomプロパティの値(=デバイスの名前)を表示した(2)。
このコードを実行すると、次のような画面になる。
 
 
 Device.Idiomプロパティの値が、iPhoneではPhoneとなり、iPadではTabletになっているのが分かる。
4. Device.OS
 Device.OSプロパティは、TargetPlatform列挙型であり、動作しているOSによって、次の値をとる。
- Android
- iOS
- WinPhone
- Other
 Device.OSプロパティの動作を確認するため、App.csファイルを以下のように修正した。
| using Xamarin.Forms; namespace WebViewSample {   ……省略……   class MyPage : ContentPage {     public MyPage() {       var label = new Label{ // <-1         XAlign = TextAlignment.Center,         YAlign = TextAlignment.Center,         Text = Device.OS.ToString() // <-2       };       if (Device.OS == TargetPlatform.iOS) {          BackgroundColor = Color.Black; // <-3         label.TextColor = Color.White;       } else if (Device.OS == TargetPlatform.Android) {         BackgroundColor = Color.White; // <-4         label.TextColor = Color.Black;       }       Content = label;     }   } } | 
 画面の中央にラベルを配置し(1)、テキストにDevice.OSプロパティの値(=OSの名前)を表示した(2)。
また、ちょっとトリッキーな例であるが、通常白色のバックとなるiOSで背景色を黒とし(3)、反対に通常黒のAndroidでは白色の背景にしてみた(4)。
このコードを実行すると、次のような画面になる。
 
 
5. Device.OnPlatform
 Device.OnPlatformメソッドは、第1パラメーターから順にiOS、Android、Windows Phoneの処理(=Actionデリゲート型の値)という3つのパラメーターをとるメソッドだ。動作しているプラットフォームによって、そのうちの1つの値を返すことで、プラットフォームごとに処理を切り分けることができる。
 Device.OnPlatformメソッドの動作を確認するため、App.csファイルを以下のように修正した。
| using Xamarin.Forms; namespace WebViewSample {     ……省略……   class MyPage : ContentPage {     public MyPage() {       Content = new StackLayout {         Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0), // <-1         Children = {new BoxView { // <-2           Color = Color.Red,           HeightRequest = 100         }}       };     }   } } | 
 スタックレイアウトの先頭に赤色のBoxViewを配置しているが(2)、iOSの場合だけ、トップに20のマージンが入るようにした(1)。
 Thicknessメソッドは、左、上、右、下の4つのパラメーターを取るが、そのうちの上の指定にDevice.OnPlatformメソッドが返す値を使用している。
iOS 7以降、画面の上部いっぱいにレイアウトすると、表示が重なってしまうため、この書式はXamarin.Formsでの定型句とも言える。
このコードを実行すると、次のような画面になる。
 
 
6. Device.OpenUri
 Device.OpenUriメソッドは、URLを指定して呼び出すだけで、それぞれのプラットフォームで標準となっているブラウザーを起動させることができる。
 Device.OpenUriメソッドの動作を確認するため、App.csファイルを以下のように修正した。
| using System; using Xamarin.Forms; namespace WebViewSample {   ……省略……   class MyPage : ContentPage {     public MyPage() {       Content = new Button { // <-1         Text = "Open",         Command = new Command(() => Device.OpenUri(new Uri("http://xamarin.com/"))) // <-2       };     }   } } | 
画面にボタンを配置し(1)、それを押したとき、ブラウザーでXamarin社のWebページを開くようにした(2)。
このコードを実行して、[Open]ボタンを押すと、次のような画面になる。
 
 
iOSでは、Safariが、Androidでは、その標準ブラウザーが起動されているのが分かる。
7. まとめ
 今回は、各プラットフォームによる違いを微調整するためのDeviceクラスについて解説した。
このクラスをうまく利用することで、Xamarin.Formsの最大のメリットであるコードの共通化が、一層楽になるだろう。
 なお、Deviceクラスには、今回紹介したものの他、Device.StartTimerメソッドやDevice.BeginInvokeOnMainThreadメソッドがあるが、この2つについては、画面などの微調整とはやや分野が違うので、次回以降にあらためて解説する。
※以下では、本稿の前後を合わせて5回分(第46回~第50回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
 
46. Xamarin.FormsでWebビューを使用するには?
外部のWebページやローカルに配置されたHTMLコンテンツを簡単に表示できるWebViewコントロールをXamarin.Formsで使う方法を説明する。
 
48. 【現在、表示中】≫ Xamarin.Formsでプラットフォームごとの微調整を行うには?
カスタムレンダラーやDependencyServiceの仕組みを使わず、Deviceクラスを利用してプラットフォーム間で異なる部分を微調整する方法を説明する。
 
49. MvvmCrossでAndroidの画面の再生成に対応するには?
Androidアプリでは別アプリ移動時に画面が破棄され、アプリ再表示時に画面が復元される場合がある。この画面の再生成を、MvxViewModelのライフサイクルメソッドにより行う方法を説明する。
 
50. Xamarin.Formsでローカルデータベースを使用するには?
アプリを終了して再起動したときに、ユーザーデータを復活させたい場合、ローカルやクラウドにデータを保存することになる。その一つの方法として、SQLite.Netを使ってローカルDBに保存する方法を説明する。






