Xamarin逆引きTips
Xamarin.Formsからプラットフォーム固有の機能を利用するには?(DependencyService利用)
UIを共通化するフレームワーク「Xamarin.Forms」で、「DependencyService」機能を使用してiOS/Androidの各プラットフォーム固有の機能を実装する方法を解説する。
Xamarin.Formsは、UIを共通にするフレームワークなので、プラットフォーム固有の機能は、それぞれ(iOS/Android)実装する必要がある。しかし、「DependencyService」と呼ばれる機能を使用することで、Xamarin.Formsから共通な呼び出しを行うことができる。今回は、このDependencyServiceの使い方を解説する。
1. Xamarin.Formsプロジェクトを作成する
プラットフォーム固有の機能を使用する例として、iOSとAndroidで、GPSの緯度経度を表示するアプリを作る。
Xamarin Studioのメニューバーの[ファイル]-[新規]-[ソリューション]から、[C#]-[Mobile Apps]-[Blank App (Xamarin.Forms Portable)]を選択し、ソリューション名を「GpsSample」として[OK]ボタンを押す。
GpsSampleプロジェクト内のApp.cs
ファイルを以下のように修正して、ボタンとラベルを配置する。
……省略……
public class App
{
public static Page GetMainPage()
{
var buttonStartGps = new Button
{
Text = "Start GPS"
};
var labelLatLon = new Label
{
};
return new ContentPage
{
Content = new StackLayout
{
Orientation = StackOrientation.Vertical,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children =
{
buttonStartGps,
labelLatLon
}
}
};
}
}
|
2. GPSを利用するための、共通なインターフェースを定義する
GpsSampleプロジェクト内にIGeoLocator.cs
ファイルを作成し、GPSを利用するための共通なIGeoLocator
インターフェースとその関連クラスを以下のように定義する。
……省略……
// イベントのパラメーター
public class LocationEventArgs : EventArgs
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
// 位置を受信した際のイベントハンドラー
public delegate void LocationEventHandler(object sender, LocationEventArgs args);
// GPS を利用するための、共通なインターフェース
public interface IGeolocator
{
void StartGps();
event LocationEventHandler LocationReceived;
}
|
Xamarin.Forms内ではこのIGeoLocator
インターフェースを介してプラットフォーム固有の機能を利用する。
3. iOSで、IGeolocatorインターフェースを実装する
GpsSample.iOSプロジェクト内に、GeoLocator_iOS.cs
ファイルを作成し、以下のように実装する。
1
2
3
|
using System;
using MonoTouch.CoreLocation;
using Xamarin.Forms;
using GpsSample.iOS;
[assembly: Dependency (typeof (GeoLocator_iOS))]
namespace GpsSample.iOS
{
public class GeoLocator_iOS : IGeolocator
{
public event LocationEventHandler LocationReceived;
private readonly CLLocationManager _locationMan = new CLLocationManager();
public void StartGps()
{
_locationMan.LocationsUpdated += (sender, e) =>
{
if (this.LocationReceived != null) {
var l = e.Locations[e.Locations.Length - 1];
this.LocationReceived(this, new LocationEventArgs
{
Latitude = l.Coordinate.Latitude,
Longitude = l.Coordinate.Longitude
});
}
};
_locationMan.StartUpdatingLocation();
}
}
}
|
まず、1が重要な要素だ。この定義により、このGeoLocator_iOS
クラスがIGeoLocator
インターフェースのiOS用の実装クラスだと宣言している。
2の箇所で、実際にGeoLocator_iOS
クラスは、IGeoLocator
インターフェースを実装している。
iOSではCLLocationManager
クラスを利用してGPSの位置を取得する。取得した位置をLocationReceived
イベントで通知するのが3だ。
【コラム】GeoLocator_iOSのクラス名
このTipsではIGeoLocator
インターフェースの実装クラス名をGeoLocator_iOS
としているが、特に命名規約はない。IGeoLocator
を実装しており、Dependency(typeof ())
属性で指定されていればどのようなクラス名でも動作する。
4. Xamarin.Formsで、IGeoLocatorオブジェクトを利用する
GpsSampleプロジェクトに戻り、App.cs
ファイルを以下のように追記する。
1
|
public class App
{
public static Page GetMainPage()
{
var buttonStartGps = new Button
{
Text = "Start GPS"
};
var labelLatLon = new Label
{
};
// 追加ここから ---
buttonStartGps.Clicked += (sender, e) =>
{
var geoLocator = DependencyService.Get<IGeolocator>();
geoLocator.LocationReceived += (_, args) =>
{
labelLatLon.Text = String.Format("{0:0.00}/{1:0.00}",
args.Latitude, args.Longitude);
};
geoLocator.StartGps();
};
// --- 追加ここまで
return new ContentPage
{
Content = new StackLayout
{
Orientation = StackOrientation.Vertical,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children =
{
buttonStartGps,
labelLatLon
}
}
};
}
}
|
いよいよDependencyService
の登場となる。ボタンが押された時に、DependencyServiceから、IGeoLocator
のインスタンスを取得する(1)。DependencyServiceはこのとき、実行中のプラットフォームに応じてIGeoLocator
の実装クラス(iOSならばGeoLocator_iOS
)をインスタンス化し返却する。あとは、この得られたインスタンスを使い、受信した緯度経度値をラベルに表示させる。
5. iOSで実行してみる
ここまでのプログラムをiOSで実行すると、次の画面のようになる。
6. Androidで、IGeolocatorインターフェースを実装する
iOSでの実行が確認できたら、次はAndroid側も実装する。GpsSample.Androidプロジェクトに、GeoLocator_Android.cs
ファイルを作成し、以下のように実装する。
1
2
4
3
|
using System;
using Xamarin.Forms;
using GpsSample.Android;
using Android.Content;
using Android.Locations;
using Android.OS;
[assembly: Dependency (typeof (GeoLocator_Android))]
namespace GpsSample.Android
{
public class GeoLocator_Android : IGeolocator
{
public event LocationEventHandler LocationReceived;
public void StartGps()
{
var context = Forms.Context;
var locationMan = context.GetSystemService(Context.LocationService)
as LocationManager;
locationMan.RequestLocationUpdates(LocationManager.GpsProvider, 0, 0,
new MyLocationListener(l =>
{
if (this.LocationReceived != null) {
this.LocationReceived(this, new LocationEventArgs
{
Latitude = l.Latitude,
Longitude = l.Longitude
});
}
}));
}
class MyLocationListener : Java.Lang.Object, ILocationListener
{
private readonly Action<Location> _onLocationChanged;
public MyLocationListener(Action<Location> onLocationChanged)
{
_onLocationChanged = onLocationChanged;
}
public void OnLocationChanged(Location location)
{
_onLocationChanged(location);
}
public void OnProviderDisabled(string provider) { }
public void OnProviderEnabled(string provider) { }
public void OnStatusChanged(string provider,
Availability status, Bundle extras) { }
}
}
}
|
1、2はiOSと同じ要領だ。AndroidではStartGps
メソッドの実装は、LocationManager
クラスを利用して行う。
位置の受信はILocationListener
インターフェースのオブジェクトで行うが、C#言語では、Java言語のように匿名クラスが使えないので、別のクラスMyLocationListener
を定義し(3)、そのコンストラクターの引数にAction
デリゲートを設定できるようにして、そのデリゲート経由でOnLocationChanged
メソッドへの位置受信結果をLocationReceived
イベントハンドラー側へ通知して、緯度経度値をラベルに表示させる。
なお、このAction
デリゲートのメソッドは、使用するときにラムダ式で記述できる(4)。この手法はXamarin.Androidではよく利用するので覚えておくとよいだろう。
7. Androidで、GPSを使うための権限を設定する
Androidでは、GPSを使用するのに権限を設定する必要がある。次の画面を参考に、AndroidManifest.xml
を開いて、[AccessFineLocation]にチェックを入れる。
8. Androidで実行する
ここまでのプログラムをAndroidで実行すると、次の画面のようになる。
まとめ
DependencyService
を利用すると、プラットフォーム固有の処理をXamarin.Formsから、共通のインターフェースを介して利用できる。これはMvvmCrossなどのフレームワークでも提供される機能であるが、Xamarinの標準機能として搭載されているのはありがたい。
DependencyService
は、実行中のプラットフォームに対応する実装クラスが見つからないときにはnull
を返す。例えばGeoLocator_Android
クラスを実装していないときにAndroidで実行すると、geoLocator
変数(=DependencyService
を使って取得したIGeolocatorオブジェクト)はnull
となり動作しないので考慮が必要だ。
Xamarin Developersの「Accessing Native Features via the DependencyService(英語)」にも情報があるので、参考にしてほしい。
※以下では、本稿の前後を合わせて5回分(第6回~第10回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
6. Xamarin.iOSでStoryboardとXamarin.Formsを併用するには?
Storyboardで作成したiOSアプリの一部の画面に、Xamarin.Formsを利用する方法を解説する。
7. Xamarin.AndroidでActivityとXamarin.Formsを併用するには?
iOSの場合と同じように、Androidアプリの一部の画面に、Xamarin.Formsを利用する方法を解説する。また、iOSとの挙動の違いやフラグメントとの併用についても言及する。
8. 【現在、表示中】≫ Xamarin.Formsからプラットフォーム固有の機能を利用するには?(DependencyService利用)
UIを共通化するフレームワーク「Xamarin.Forms」で、「DependencyService」機能を使用してiOS/Androidの各プラットフォーム固有の機能を実装する方法を解説する。
9. Xamarin.Formsでダイアログボックス(とBusyインジケーター)を表示するには?
ダイアログ(=iOSのUIAlertView/AndroidのAlertDialog)や、処理実行中を示すBusyインジケーターを、Xamarin.Formsで表示する方法を解説する。これらは共通のAPIを使って実装できる。
10. Xamarin.Formsでカスタムダイアログを表示するには?(MessagingCenter利用)
Xamarin.Formsで共通のAPIが提供されているダイアログ(=iOSのUIAlertView/AndroidのAlertDialog)以外のプラットフォーム個別のダイアログを表示する方法を解説。その呼び出しにはMessagingCenterが利用できる。