Xamarin.Forms の MapView について

# はじめに

明日 2014/6/11(水) から INTEROP と同時開催のロケーションビジネスジャパン内で GeoMediaSummit の LT 大会が開かれます。喋ってみませんか？と誘われましたので、せっかくの機会ですし、Xamarin.Forms でマップアプリ作って持って行こうと思い頑張りました。

結構嵌ってしまいましたので、備忘録として残します。

## iOS/Android 両方

- MapView を実装するには NuGet で各プロジェクトに `Xamarin.Forms.Maps` を追加します。<br />
![スクリーンショット 2014-06-10 12.04.40.png](https://qiita-image-store.s3.amazonaws.com/0/32453/5b2efa43-7726-ee26-4247-6ff0dc2bd7b7.png)
- [Xamarin.Forms.Maps の API リファレンス](http://iosapi.xamarin.com/?link=N%3AXamarin.Forms.Maps) には、`AppDelegate.cs` と `MainActivity.cs` でそれぞれ `FormsMaps.Init` してね。とありますが、そもそも `Forms.Maps.Init` じゃないんかい！とツッコミを入れつつ、かつ、`using Xamarin.Forms;` と
`using Xamarin.Forms.Maps;` を
しても `Xamarin.FormsMaps.Init` と呼び出さなければいけないという不思議な現象を軽く乗り越えます。

マップの実装のサンプルです。非常に簡単ですね。
[API リファレンス](http://iosapi.xamarin.com/?link=N%3AXamarin.Forms.Maps) や [Form Gallery サンプルアプリ](https://github.com/xamarin/xamarin-forms-samples/blob/master/FormsGallery/FormsGallery/FormsGallery/MapDemoPage.cs) などを参考にしてください。

```csharp:App.cs
public static Page GetMainPage ()
{	
	Map map = new Map ();
	Position position = new Position (35.681382, 139.766084);
	map.MoveToRegion (new MapSpan (position,0.01,0.01));
	map.Pins.Add (new Pin 
		{
			Label = "東京駅はここです",
			Position = position,
		});
	return new ContentPage { 
		Content = new StackLayout {
			Children = {
				map,
			},
		},
	};
}
```

```csharp:App.cs
public static Page GetMainPage ()
{	
	return new ContentPage { 
		Content = new Map (MapSpan.FromCenterAndRadius (new Position (35.681382, 139.766084), Distance.FromKilometers (0.3)))
	};
}
```



## iOS アプリ

ビルドするとこんな感じです。
![スクリーンショット 2014-06-10 20.36.25.png](https://qiita-image-store.s3.amazonaws.com/0/32453/62f6a4dc-3a75-dc81-bc6f-dc2d67e40184.png)


## Android アプリ

少し大変です。

- API Key が必要です。[Google Developer Console](https://console.developers.google.com) で API Key を生成します。
- ちゃんと Xamarin で Android のマップアプリを開発した方なら一度は読んでいるであろう [API Key の作り方](http://developer.xamarin.com/guides/android/platform_features/maps_and_location/maps/obtaining_a_google_maps_api_key/) を読んで、 **正しい** debug.keystore から API Key を作りますｗ 
- API Key は Developer Console さんの指示通り `keytool -list -v -keystore debug.keystore -storepass android` で表示される SHA1 のハッシュを元に作成します。
- なお、Xamarin でデバッグする時に使われる debug.keystore は標準では `Windows - C:\Users\[USERNAME]\AppData\Local\Xamarin\Mono for Android\debug.keystore` と
`OSX - /Users/[USERNAME]/.local/share/Xamarin/Mono for Android/debug.keystore` にあります。eclipse とは違うので半日ほどハマらないように注意してくださいｗ
- 生成した API_KEY を利用して一般的な Android アプリと同じように Permission などを設定します。なんか最後の ACCESS_WIFI_STATE も付けとくと Wifi 経由のデバッグの時に救われたりするようです。 

```xml:AndroidManifest.xml
<application android:label="XamarinForms_MapView01_Android">
	<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="xxxxxxxxxxAPI_KEYxxxxxxxxxx" />
</application>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<permission android:name="com.example.XamarinForms_MapView01_Android.permission.MAPS_RECEIVE" android:protectionLevel="signature" />
<uses-permission android:name="com.example.XamarinForms_MapView01_Android.permission.MAPS_RECEIVE" />

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
```


後は小ネタですが、

- Xamarin.Forms のソリューションを作成すると、標準で "アプリ名.Android" の Android プロジェクトが作成されます。パッケージ名も同じく "アプリ名.Android" になっています。これ、良くないみたいなので、"アプリ名_Android" とかにした方が良いかもです。 [ここに](http://qiita.com/tomo_klotho/items/d2386cb473ed011c9a89) 書いてありました。
- Mac の Xamarin Studio で開発している場合は、Xamarin.Forms アプリをテンプレートから作成すると、Target Framework が 4.0.3 になっていると思います。この状態で Xamarin.Forms.Maps を追加すると、<br />![スクリーンショット 2014-06-10 20.51.46.png](https://qiita-image-store.s3.amazonaws.com/0/32453/770d8e6e-ac7b-9c78-4dd7-98d042519d6d.png)<br />とライブラリがいくつか追加されます。この中の  Xamarin.Android.Support.v7.MediaRouter が 4.2 以上の Target Framework でないとビルド時に大量の `error: No resource identifier found for attribute 'paddingStart' in package 'android'` のようなエラーを吐きますので、Target Framework を latest に、Minimum を 4.0.3 などにしておくと良さそうです。Visual Studio で作成した場合は、Latest と Minimum がちゃんと設定されていました。

- Windows の Visual Studio でビルドした時は出ませんでしたが、Mac の Xamarin Studio でビルドした時に以下のエラーが出ました。Heapsize が足りないそうです。マシンのメモリ状況にもよるかもしれません。


>/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets: Error: Tool exited with code: 3. Output: 
UNEXPECTED TOP-LEVEL ERROR:
java.lang.OutOfMemoryError: Java heap space
	at java.util.ArrayList.<init>(ArrayList.java:112)
	at java.util.ArrayList.<init>(ArrayList.java:119)
	at com.android.dx.ssa.SsaBasicBlock.<init>(SsaBasicBlock.java:124)
	at com.android.dx.ssa.SsaBasicBlock.newFromRop(SsaBasicBlock.java:147)
	at com.android.dx.ssa.SsaMethod.convertRopToSsaBlocks(SsaMethod.java:173)
	at com.android.dx.ssa.SsaMethod.newFromRopMethod(SsaMethod.java:103)
	at com.android.dx.ssa.SsaConverter.convertToSsaMethod(SsaConverter.java:44)
	at com.android.dx.ssa.Optimizer.optimize(Optimizer.java:98)
	at com.android.dx.ssa.Optimizer.optimize(Optimizer.java:72)
	at com.android.dx.dex.cf.CfTranslator.processMethods(CfTranslator.java:303)
	at com.android.dx.dex.cf.CfTranslator.translate0(CfTranslator.java:139)
	at com.android.dx.dex.cf.CfTranslator.translate(CfTranslator.java:94)
	at com.android.dx.command.dexer.Main.processClass(Main.java:682)
	at com.android.dx.command.dexer.Main.processFileBytes(Main.java:634)
	at com.android.dx.command.dexer.Main.access$600(Main.java:78)
	at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:572)
	at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
	at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
	at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
	at com.android.dx.command.dexer.Main.processOne(Main.java:596)
	at com.android.dx.command.dexer.Main.processAllFiles(Main.java:498)
	at com.android.dx.command.dexer.Main.runMonoDex(Main.java:264)
	at com.android.dx.command.dexer.Main.run(Main.java:230)
	at com.android.dx.command.dexer.Main.main(Main.java:199)
	at com.android.dx.command.Main.main(Main.java:103)
 (XamarinForms_MapView01.Android)


適当に
![スクリーンショット 2014-06-10 16.44.15.png](https://qiita-image-store.s3.amazonaws.com/0/32453/7d73d2b1-e5c3-0d19-f49b-6dbbbaef9157.png)
とか設定します。(ビルドのオプションだと思うので、大きくても問題ないですよね？)

多分これくらいで大体のエラーは回避できるのではないかと思います。Android アプリはこんな感じです。
![スクリーンショット 2014-06-10 21.17.21.png](https://qiita-image-store.s3.amazonaws.com/0/32453/34fc6ebd-97f0-1061-c401-1cff6717dd85.png)


以上です。これで LT のネタが出来ました。良かったです。