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が利用できる。