検索
特集

サンプルプログラムで理解するXamarin.Formsの特徴特集:Xamarin.Formsを知る(3/3 ページ)

現在、大きな注目を集めているXamarin.Formsとは何か。サンプルコードを解剖しながら、その特徴を調べてみよう。

Share
Tweet
LINE
Hatena
前のページへ |       

共有コード

 以下では3つのアプリプロジェクトで共有されるコードの中でポイントとなる部分を幾つか紹介する。

XAML

 まずは3つのアプリプロジェクトで共有されるUIから見てみよう。

<?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="Phoneword.MainPage">
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="20, 40, 20, 20" Android="20, 20, 20, 20" WinPhone="20, 20, 20, 20" />
  </ContentPage.Padding>
  <ContentPage.Content>
    <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Orientation="Vertical" Spacing="15">
      <Label Text="Enter a Phoneword:" />
      <Entry x:Name="phoneNumberText" Text="1-855-XAMARIN" />
      <Button x:Name="translateButon" Text="Translate" Clicked="OnTranslate" />
      <Button x:Name="callButton" Text="Call" IsEnabled="false" Clicked="OnCall" />
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

PhonewordアプリのUI(XAML)

 見慣れたXAMLコードだ。ただし、スキーマとして「http://xamarin.com/schemas/2014/forms」が含まれていることに注意。これによりXamarin.Formsで定義されている要素をXAMLに記述できるように名前空間が導入されている(強調表示部分。もう1つの強調表示部分については後述する)。

 細かな要素から順に見ていくと、このUIには1つのラベル(<Label>要素)、1つのデータ入力ボックス(<Entry>要素)、2つのボタン(<Button>要素)が含まれている。これらは<StackLayout>要素に含まれていて、そのOrientation属性が「Vertical」になっているので、縦に4つのUI要素がレイアウトされることになる。そして、<StackLayout>要素が<ContentPage>要素のContent属性に含まれていて、このページの内容(コンテンツ)が<StackLayout>要素で表されていることが分かる。この辺の構造は通常のXAMLと同様だ。

XAMLで記述したUIの構造
XAMLで記述したUIの構造
図中の「ページ」「レイアウト」「ビュー」については後述する。

 今述べた要素は全てXamarin.Formsで定義されている「コントロール」だ。冒頭で述べたようにXamarin.FormsはiOS、Android、Windows(ここではUWP)のUIを抽象化したレイヤーであり、Xamarin.Formsで定義されているコントロールは、アプリの実行時にはそれぞれのOSにネイティブなコントロールを使用して描画が行われる。

Xamarin.Formsのコントロールは実際にはOSにネイティブなコントロールを使って描画される
Xamarin.Formsのコントロールは実際にはOSにネイティブなコントロールを使って描画される
左はiPhone Emulatorでの表示。中はVisual Studio Emulator for Androidでの表示。右はWindows 10 Mobileエミュレーターでの表示。
[Translate]ボタンと[Call 〜]ボタンに注目すると、iOSアプリではボタンの境界線がないことに、Androidアプリでは文字が全て大文字化されていることが分かる。テキストボックスに枠線があるのはUWPアプリだけだ。XAMLは共通しても、各OSにネイティブなコントロールで(今回はデフォルトの設定を使って)描画が行われているために、このような差異が出てくる。

 これらのコントロールは大きく以下の4種類に分類されている(前掲の図も参照のこと)。

  • ページ: 画面全体を占拠するUI要素。上のXAMLでは<ContentPage>要素が該当
  • レイアウト: 他のレイアウトやビューのコンテナとなり、それらをどう表示するのかのレイアウトを指定する。上のXAMLでは<StackLayout>要素が該当
  • ビュー: ボタンやラベル、テキストボックスなどの一般的なコントロール。上のXAMLでは<Button>要素、<Label>要素、<Entry>要素が該当
  • セル: リストビューや表中に要素を表示するために使われる。例えば、EntryCellコントロールは「ラベル+1行入力テキストエントリ」としてXamarin.Formsでは扱われ、iOSでは「UITableViewCellとUITextField」、Androidでは「TextViewとEditTextを含んだLinearLayout」、UWPでは「TextBoxを含んだデータテンプレート」を使って描画される

 このように、3つのアプリプロジェクトではXAMLを共有するが、実際のアプリの画面表示ではOSにネイティブなUI要素が使われる。またXAMLは共有されるが、その中でOS依存の設定を行うこともできる。これを行っているのが以下のコードだ(コードが見やすくなるように改行を挿入している)。

<ContentPage.Padding>
  <OnPlatform x:TypeArguments="Thickness"
                iOS="20, 40, 20, 20"
                Android="20, 20, 20, 20"
                WinPhone="20, 20, 20, 20" />
</ContentPage.Padding>

OS依存の設定をXAMLで行う

 これはページに表示するコンテンツとページ境界との間にどれだけの空きを含めるかを指定するXAMLだが、<OnPlatform>属性を用いてiOSだけは空きの量を変更している(「40」に注目)。これはOSごとに異なる挙動を記述する1つの方法だ。C#コードでも「Device.OnPlatform」メソッドを使うことで、OSによって処理を変更できる。

IDialerインタフェース

 共有されるコードでもう1つ重要な部分を以下に示す。これは、上で示した実行画面で[Call]ボタンがクリックされたときに実行されるコードだ。

public partial class MainPage : ContentPage
{
  string translatedNumber;

  public MainPage()
  {
    InitializeComponent();
  }

  void OnTranslate(object sender, EventArgs e)
  {
    …… Translate]ボタンがクリックされたら、Phone wordsを数字に変換する ……
  }

  async void OnCall(object sender, EventArgs e)
  {
    if (await this.DisplayAlert(
          "Dial a Number",
          "Would you like to call " + translatedNumber + "?",
          "Yes",
          "No"))
    {
      var dialer = DependencyService.Get<IDialer>();
      if (dialer != null)
        dialer.Dial(translatedNumber);
    }
  }
}

DependencyServiceクラスを利用して、OSごとに実装されているIDialerインタフェースを取得する

 PCLプロジェクトは、3つのアプリプロジェクトから参照されるだけで、PCLからアプリを参照はしない。そのため、アプリプロジェクトで実装するOS依存の機能はこのコードのようにDependencyServiceクラスを利用して、依存性注入の形で呼び出す。先も見た通り、IDialerインタフェースはdialメソッドのみを持つインタフェースだ。そして、これを実装するのが次に紹介する3つのPhoneDialer.csファイルだ。

共有しないコード

 3つのアプリプロジェクトではIDialerインタフェースを実装するPhoneDialerクラスを定義している。スマートフォンでは電話をかけるためのコードはOSごとに異なる。そのため、各アプリプロジェクトで個別に実装する必要がある。例えば、Androidではインテントを使用して、以下のようなコードを記述する。

using Android.Content;
using Android.Telephony;
using Phoneword.Droid;
using System.Linq;
using Xamarin.Forms;

using Uri = Android.Net.Uri;

[assembly: Dependency(typeof(PhoneDialer))]

namespace Phoneword.Droid
{
  public class PhoneDialer : IDialer
  {
    public bool Dial(string number)
    {
      var context = Forms.Context;
      if (context == null)
        return false;

      var intent = new Intent(Intent.ActionCall);
      intent.SetData(Uri.Parse("tel:" + number));

      if (IsIntentAvailable(context, intent))
      {
        context.StartActivity(intent);
        return true;
      }
      return false;
    }

    public static bool IsIntentAvailable(Context context, Intent intent)
    {
      …… 中略 ……
    }
  }
}

AndroidアプリでのIDialerインタフェース実装

 このようにOSが提供する機能を使ってインタフェースを実装すると共に、assembly属性でDependencyServiceクラスに対して登録を行う(強調表示部分)。このようにすることで、上で見た「DependencyService.Get<IDialer>()」というコードでこのPhoneDialerクラスのインスタンスが取得できる。後は、そのdialメソッドを呼び出すだけだ。他のOSでのIDialerインタフェース実装については省略するが、実装がどんなコードになるかはともかく基本的な考えはこれと同様だ。

まとめ

 本稿では、Xamarinが提供するサンプルプログラム「Phoneword」のコードを分解しながら、Xamarin.Formsアプリの特徴を幾つか見てきた。ポイントを以下にまとめておく。

  • PCLプロジェクトにはアプリプロジェクトで共有できるロジック、UI、OSごとに異なる実装を抽象化したインタフェースなどをまとめる
  • Xamarin.Formsは独自のコントロールを提供し、それらはアプリ実行時にそれをホストするOSにネイティブなコントロールを用いて描画される
  • XAMLの内部でもOSごとに設定を切り替えられる(<OnPlatform>要素)
  • Device.OnPlatformメソッドを使うと、OSごとに処理を切り替えられる
  • ある機能をOSごとに個別に実装する場合、PCL側ではインタフェースを定義すると共にDependencyServiceクラスを用いて、そのインタフェース実装のインスタンスを取得する
  • アプリプロジェクト側では、OSが提供する機能を用いて、その機能を実装し、assembly属性を用いてDependencyServiceに登録を行うことで、PCL側がその実装を取得できるようにする

 本稿では紹介しきれなかったが、自分でOSネイティブなコントロールを利用したり、Xamarin.Formsのコントロールのカスタマイズを行ったりする機構としてはカスタムレンダラーも用意されている。なお、次回以降では、Xamarin.Formsプログラミングのポイントをより詳細に紹介していく予定だ。

「特集:Xamarin.Formsを知る」のインデックス

特集:Xamarin.Formsを知る

Copyright© Digital Advantage Corp. All Rights Reserved.

前のページへ |       
ページトップに戻る