「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にマーカーや吹き出しを表示する方法を解説する。
前回に引き続き、Xamarin.AndroidアプリでGoogle Maps Android API v2(以下、「GoogleMapAPI」と表記)を使用する。今回はマーカーや吹き出しを表示する方法を解説する。
「Tips:Xamarin.Androidで地図に図形を表示するには?(Google Maps使用)」の続きとして解説するので、プロジェクトの準備は前回を参照してほしい。
前回終了時点で、Main.axmlファイルを開くと、以下のようになっているはずだ。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.google.android.gms.maps.MapFragment"/> <Button android:id="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:text="Center"/> <Button android:id="@+id/buttonBounds" android:layout_toRightOf="@+id/buttonCenter" android:layout_alignTop="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bounds"/> <Button android:id="@+id/buttonCamera" android:layout_toRightOf="@+id/buttonBounds" android:layout_alignTop="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Camera"/> <Button android:id="@+id/buttonLine" android:layout_alignLeft="@+id/buttonCenter" android:layout_above="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Line"/> <Button android:id="@+id/buttonPolygon" android:layout_alignLeft="@+id/buttonBounds" android:layout_above="@+id/buttonBounds" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Polygon"/> <Button android:id="@+id/buttonCircle" android:layout_alignLeft="@+id/buttonCamera" android:layout_above="@+id/buttonCamera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Circle"/> </RelativeLayout> |
これを、以下のように修正してボタンを追加する。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.google.android.gms.maps.MapFragment"/> <Button android:id="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:text="Center"/> <Button android:id="@+id/buttonBounds" android:layout_toRightOf="@+id/buttonCenter" android:layout_alignTop="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bounds"/> <Button android:id="@+id/buttonCamera" android:layout_toRightOf="@+id/buttonBounds" android:layout_alignTop="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Camera"/> <Button android:id="@+id/buttonLine" android:layout_alignLeft="@+id/buttonCenter" android:layout_above="@+id/buttonCenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Line"/> <Button android:id="@+id/buttonPolygon" android:layout_alignLeft="@+id/buttonBounds" android:layout_above="@+id/buttonBounds" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Polygon"/> <Button android:id="@+id/buttonCircle" android:layout_alignLeft="@+id/buttonCamera" android:layout_above="@+id/buttonCamera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Circle"/> <!-- 追加ここから --> <Button android:id="@+id/buttonAddMarker" android:layout_alignLeft="@+id/buttonLine" android:layout_above="@+id/buttonLine" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Add Marker"/> <Button android:id="@+id/buttonDeleteMarker" android:layout_alignLeft="@+id/buttonCircle" android:layout_above="@+id/buttonCircle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Delete Marker"/> <!-- 追加ここまで --> </RelativeLayout> |
これをUIデザイナーで確認すると、図1のようになる。
前回までの画面にある[Line][Polygon][Circle]ボタンの上に、それぞれ[Add Marker][Delete Marker]ボタンを配置している。
MainActivity.csファイルに、[Add Marker]ボタンを押した時に「東京スカイツリー」と「東京タワー」の場所にそれぞれマーカーを追加するコードを、以下のように記述する。
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")] publicclassMainActivity:Activity { protectedoverridevoidOnCreate(Bundlebundle) { base.OnCreate(bundle); ……省略…… Markermarker1=null; Markermarker2=null; FindViewById<Button>(Resource.Id.buttonAddMarker).Click+=(sender,e)=> { if(marker1==null) { marker1=map.AddMarker(newMarkerOptions() .SetTitle("東京スカイツリー") .SetSnippet("東京都墨田区押上1−1−2") .SetPosition(newLatLng(35.710063d,139.8107d)) .InvokeIcon(BitmapDescriptorFactory.DefaultMarker( BitmapDescriptorFactory.HueAzure)) //<--1 ); } if(marker2==null) { marker2=map.AddMarker(newMarkerOptions() .SetTitle("東京タワー") .SetSnippet("東京都港区芝公園4−2−8") .SetPosition(newLatLng(35.65858,139.745433)) .InvokeIcon(BitmapDescriptorFactory.DefaultMarker( BitmapDescriptorFactory.HueOrange)) //<--2 ); } }; } } |
1で、[Add Marker]ボタンを押した時に、map.AddMarkerメソッドを呼び出して、東京スカイツリーの位置に、マーカーを追加している。引数はMarkerOptionで、このオブジェクトにさまざまなパラメーターを設定する。SetTitleメソッドとSetSnippetメソッドで指定しているのは、後述するマーカーの吹き出しに表示されるタイトルと説明文だ。SetPositionメソッドでマーカーの位置を、InvokeIconメソッドでマーカーの画像を指定する。ここではBitmapDescriptorFactory.DefaultMarkerメソッドで、既定のアイコンの色だけを変更したものを使用している。
アイコンを変更するには、BitmapDescriptorFactory.FromResourceなどのメソッドを使用するが、詳細についてはここでは述べない。ネイティブのGoogleMapAPIのAPIリファレンスを参考にしてほしい。
map.AddMarkerメソッドでマーカーを追加すると、その戻り値としてMarkerオブジェクトが返却される。これは、後述するマーカーの削除やマーカーが選択された時の識別子として利用する。
2は、1と同じ方法で、東京タワーの位置にマーカーを追加している。
次に、追加したマーカーを削除する方法を解説する。
[Delete Marker]ボタンを押した時に「東京スカイツリー」のマーカーのみを削除するコードを、以下のように記述する。
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")] publicclassMainActivity:Activity { protectedoverridevoidOnCreate(Bundlebundle) { base.OnCreate(bundle); ……省略…… Markermarker1=null; Markermarker2=null; ……省略…… FindViewById<Button>(Resource.Id.buttonDeleteMarker).Click+=(sender,e)=> { if(marker1!=null) { marker1.Remove(); marker1=null; } }; } } |
マーカーの削除は、PolylineやPolygonなどと同じく、MarkerオブジェクトのRemoveメソッドを呼び出すだけだ。
ここまでのプログラムを実行すると、図2のようになる。
[Add Marker]ボタンを押すと、東京スカイツリーの位置に青色のマーカーが、さらに東京タワーの位置にオレンジ色のマーカーがそれぞれ表示される。
[Delete Marker]ボタンを押すと、東京スカイツリーのマーカーが削除される。
続いて、マーカーをタップして選択した時に何らかの処理を行う方法を解説する。
MyActivity.csファイルに以下のようにコードを追加する。
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")] publicclassMainActivity:Activity { protectedoverridevoidOnCreate(Bundlebundle) { base.OnCreate(bundle); ……省略…… Markermarker1=null; Markermarker2=null; ……省略…… map.MarkerClick+=(objectsender,GoogleMap.MarkerClickEventArgse)=>//<--1 { Toast.MakeText(this, e.Marker.Title+"のマーカーがタップされました。", ToastLength.Short).Show();//<--2 e.Handled=false;//<--3 }; } } |
マーカーが選択されると、GoogleMapオブジェクトのMarkerClickイベントが発生する(1)。イベントパラメーターであるMarkerClickEventArgsオブジェクトから、クリックされたMarkerオブジェクトが取得できるので、そのTitleプロパティ値を使って「○○のマーカーがタップされました。」というToastを表示している(2)。
最後の3は、GoogleMapAPI側でマーカーがタップされた後の処理を行うかどうかだ。マーカーを選択した際の既定の動作は、「マーカーにセンタリングし、吹き出しを表示する」だが、MarkerClickEventArgsオブジェクトのHandledプロパティをtrueに設定すると、既定の動作が抑制される。MarkerClickEventArgs.Handledプロパティの既定値はtrueであり、明示的にfalseを設定しないと、吹き出しの表示などが行われなくなるので注意が必要だ。
ここまでのプログラムを実行すると、図3のようになる。
マーカーを選択すると、吹き出しとToastが表示される。
最後に、マーカーの上の吹き出しがタップされた時に処理を行う方法について解説する。
MyActivity.csファイルに以下のようにコードを追加する。
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")] publicclassMainActivity:Activity { protectedoverridevoidOnCreate(Bundlebundle) { base.OnCreate(bundle); ……省略…… Markermarker1=null; Markermarker2=null; ……省略…… map.InfoWindowClick+=(objectsender,GoogleMap.InfoWindowClickEventArgse)=>//<--1 { Toast.MakeText(this, e.Marker.Title+"の吹き出しがタップされました。", ToastLength.Short).Show();//<--2 }; } } |
吹き出しをタップすると、GoogleMapオブジェクトのInfoWindowClickイベントが発生する(1)。イベントパラメーターであるInfoWindowClickEventArgsオブジェクトから、クリックされたMarkerオブジェクトが取得できるので、あとはマーカーの選択と同じように、「○○の吹き出しがタップされました。」というToastを表示している(2)。
ここまでのプログラムを実行すると、図4のようになる。
マーカーを選択すると吹き出しが表示され、吹き出しをタップすると「吹き出しがタップされました」と表示される。
Xamarin.Androidで地図を表示するには?(Google Maps使用)から今回までの数回で、Xamarin.AndroidでのGoogleMapAPIの基本的な使用方法を解説した。
再掲になるが、Xamarin Developer Center(英語)の、
および、GoogleMapAPIのネイティブの解説である
も参考にされたい。
また、クロスプラットフォームでUIを共通化できるXamarin.Formsでは、Xamarin.iOSではMapKit、Xamarin.AndroidではGoogleMapAPI、Windows PhoneではBing Mapsと、プラットフォームごとに異なるAPIを抽象化し、共通なAPIを用意している。こちらについては別の機会に解説する予定であるが、併せて参照するとよいだろう。
※以下では、本稿の前後を合わせて5回分(第21回~第25回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図の表示位置や種類を変更する方法、ジェスチャの有効/無効を切り替える方法などを説明する。
「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にライン/ポリゴン/円などの図形を表示する方法を解説する。
「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にマーカーや吹き出しを表示する方法を解説する。
Xamarin Studioの新機能として、コードエディターを左右の領域に分割して表示できるようになった。マウスとショートカットキーによる操作方法を説明する。
Xamarin.AndroidでネイティブAndroidのLogクラスを使ってログ出力する方法と、Xamarin.iOS/Androidで.NET基本クラスライブラリ機能を使ってログ出力する方法を解説する。