Xamarin逆引きTips

Xamarin逆引きTips

Xamarin.Androidで地図にマーカーを表示するには?(Google Maps使用)

2014年10月22日

「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にマーカーや吹き出しを表示する方法を解説する。

奥山 裕紳(@amay077
  • このエントリーをはてなブックマークに追加

 前回に引き続き、Xamarin.AndroidアプリでGoogle Maps Android API v2(以下、「GoogleMapAPI」と表記)を使用する。今回はマーカーや吹き出しを表示する方法を解説する。

 「Tips:Xamarin.Androidで地図に図形を表示するには?(Google Maps使用)」の続きとして解説するので、プロジェクトの準備は前回を参照してほしい。

1. 画面を準備する

 前回終了時点で、Main.axmlファイルを開くと、以下のようになっているはずだ。

AXML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns: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>
前回終了時点のUIデザイン内容(Main.axml)

 これを、以下のように修正してボタンを追加する。

AXML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns: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デザイン内容(Main.axml)

 これをUIデザイナーで確認すると、図1のようになる。

図1 ボタンを追加したMain.axml

 前回までの画面にある[Line][Polygon][Circle]ボタンの上に、それぞれ[Add Marker][Delete Marker]ボタンを配置している。

2. マーカーを追加する

 MainActivity.csファイルに、[Add Marker]ボタンを押した時に「東京スカイツリー」と「東京タワー」の場所にそれぞれマーカーを追加するコードを、以下のように記述する。

C#
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
  protected override void OnCreate(Bundle bundle)
  {
    base.OnCreate(bundle);

    ……省略……

    Marker marker1 = null;
    Marker marker2 = null;

    FindViewById<Button>(Resource.Id.buttonAddMarker).Click += (sender, e) => 
    {
      if (marker1 == null) 
      {
        marker1 = map.AddMarker(new MarkerOptions()
          .SetTitle("東京スカイツリー")
          .SetSnippet("東京都墨田区押上1−1−2")
          .SetPosition(new LatLng(35.710063d, 139.8107d))
          .InvokeIcon(BitmapDescriptorFactory.DefaultMarker(
            BitmapDescriptorFactory.HueAzure))  //<--1
        );
      }

      if (marker2 == null)
      {
        marker2 = map.AddMarker(new MarkerOptions()
          .SetTitle("東京タワー")
          .SetSnippet("東京都港区芝公園4−2−8")
          .SetPosition(new LatLng(35.65858,139.745433))
          .InvokeIcon(BitmapDescriptorFactory.DefaultMarker(
            BitmapDescriptorFactory.HueOrange))  //<--2
        );
      }
    };
  }
}
[Add Marker]ボタンを押した時にマーカーを追加するコード(MainActivity.cs)

 1で、[Add Marker]ボタンを押した時に、map.AddMarkerメソッドを呼び出して、東京スカイツリーの位置に、マーカーを追加している。引数はMarkerOptionで、このオブジェクトにさまざまなパラメーターを設定する。SetTitleメソッドとSetSnippetメソッドで指定しているのは、後述するマーカーの吹き出しに表示されるタイトル説明文だ。SetPositionメソッドでマーカーの位置を、InvokeIconメソッドでマーカーの画像を指定する。ここではBitmapDescriptorFactory.DefaultMarkerメソッドで、既定のアイコンの色だけを変更したものを使用している。

 アイコンを変更するには、BitmapDescriptorFactory.FromResourceなどのメソッドを使用するが、詳細についてはここでは述べない。ネイティブのGoogleMapAPIのAPIリファレンスを参考にしてほしい。

 map.AddMarkerメソッドでマーカーを追加すると、その戻り値としてMarkerオブジェクトが返却される。これは、後述するマーカーの削除やマーカーが選択された時の識別子として利用する。

 2は、1と同じ方法で、東京タワーの位置にマーカーを追加している。

3. マーカーを削除する

 次に、追加したマーカーを削除する方法を解説する。

 [Delete Marker]ボタンを押した時に「東京スカイツリー」のマーカーのみを削除するコードを、以下のように記述する。

C#
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
  protected override void OnCreate(Bundle bundle)
  {
    base.OnCreate(bundle);

    ……省略……

    Marker marker1 = null;
    Marker marker2 = null;

    ……省略……

    FindViewById<Button>(Resource.Id.buttonDeleteMarker).Click += (sender, e) =>
    {
      if (marker1 != null)
      {
        marker1.Remove();
        marker1 = null;
      }
    };
  }
}
[Delete Marker]ボタンを押した時にマーカーを削除するコード(MainActivity.cs)

 マーカーの削除は、PolylinePolygonなどと同じく、MarkerオブジェクトのRemoveメソッドを呼び出すだけだ。

 ここまでのプログラムを実行すると、図2のようになる。

図2 プログラムの実行結果(マーカーの表示)

 [Add Marker]ボタンを押すと、東京スカイツリーの位置に青色のマーカーが、さらに東京タワーの位置にオレンジ色のマーカーがそれぞれ表示される。

 [Delete Marker]ボタンを押すと、東京スカイツリーのマーカーが削除される。

4. マーカーが選択された時に処理を行う

 続いて、マーカーをタップして選択した時に何らかの処理を行う方法を解説する。

 MyActivity.csファイルに以下のようにコードを追加する。

C#
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
  protected override void OnCreate(Bundle bundle)
  {
    base.OnCreate(bundle);

    ……省略……

    Marker marker1 = null;
    Marker marker2 = null;

    ……省略……

    map.MarkerClick += (object sender, GoogleMap.MarkerClickEventArgs e) => //<--1
    {
      Toast.MakeText(this, 
        e.Marker.Title + "のマーカーがタップされました。",
        ToastLength.Short).Show(); //<--2

      e.Handled = false; //<--3
    };
  }
}
マーカーをタップして選択した時にToastを表示するコード(MyActivity.cs)

 マーカーが選択されると、GoogleMapオブジェクトのMarkerClickイベントが発生する(1)。イベントパラメーターであるMarkerClickEventArgsオブジェクトから、クリックされたMarkerオブジェクトが取得できるので、そのTitleプロパティ値を使って「○○のマーカーがタップされました。」というToastを表示している(2)。

 最後の3は、GoogleMapAPI側でマーカーがタップされた後の処理を行うかどうかだ。マーカーを選択した際の既定の動作は、「マーカーにセンタリングし、吹き出しを表示する」だが、MarkerClickEventArgsオブジェクトのHandledプロパティをtrueに設定すると、既定の動作が抑制される。MarkerClickEventArgs.Handledプロパティの既定値はtrueであり、明示的にfalseを設定しないと、吹き出しの表示などが行われなくなるので注意が必要だ。

 ここまでのプログラムを実行すると、図3のようになる。

図3 プログラムの実行結果(マーカーを選択したとき)

 マーカーを選択すると、吹き出しとToastが表示される。

5. 吹き出しが押された時に処理を行う

 最後に、マーカーの上の吹き出しがタップされた時に処理を行う方法について解説する。

 MyActivity.csファイルに以下のようにコードを追加する。

C#
[Activity(Label = "GoogleMapSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
  protected override void OnCreate(Bundle bundle)
  {
    base.OnCreate(bundle);

    ……省略……

    Marker marker1 = null;
    Marker marker2 = null;

    ……省略……

    map.InfoWindowClick += (object sender, GoogleMap.InfoWindowClickEventArgs e) => //<--1
    {
      Toast.MakeText(this, 
        e.Marker.Title + "の吹き出しがタップされました。", 
        ToastLength.Short).Show(); //<--2
    };
  }
}
マーカーの上の吹き出しがタップされた時にToastを表示するコード(MyActivity.cs)

 吹き出しをタップすると、GoogleMapオブジェクトのInfoWindowClickイベントが発生する(1)。イベントパラメーターであるInfoWindowClickEventArgsオブジェクトから、クリックされたMarkerオブジェクトが取得できるので、あとはマーカーの選択と同じように、「○○の吹き出しがタップされました。」というToastを表示している(2)。

 ここまでのプログラムを実行すると、図4のようになる。

図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]を参照してください。

21. Xamarin.Androidで地図を操作するには?(Google Maps使用)

「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図の表示位置や種類を変更する方法、ジェスチャの有効/無効を切り替える方法などを説明する。

22. Xamarin.Androidで地図に図形を表示するには?(Google Maps使用)

「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にライン/ポリゴン/円などの図形を表示する方法を解説する。

23. 【現在、表示中】≫ Xamarin.Androidで地図にマーカーを表示するには?(Google Maps使用)

「Google Maps Android API v2」を使って、Xamarin.Androidアプリで地図上にマーカーや吹き出しを表示する方法を解説する。

24. Xamarin Studio で画面を分割するには?(ショートカットキー情報あり)[Mac&Win対応]

Xamarin Studioの新機能として、コードエディターを左右の領域に分割して表示できるようになった。マウスとショートカットキーによる操作方法を説明する。

25. Xamarin.iOS/Androidでログを出力するには?

Xamarin.AndroidでネイティブAndroidのLogクラスを使ってログ出力する方法と、Xamarin.iOS/Androidで.NET基本クラスライブラリ機能を使ってログ出力する方法を解説する。

イベント情報(メディアスポンサーです)

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • 日本マイクロソフト株式会社
  • グレープシティ株式会社