Android Tips #26 Google Maps Android API v2 のマーカーをカスタマイズする

catch

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

前回の記事でご紹介したマーカーのカスタマイズ方法について、もう少し詳細に解説してみたいと思います。以下のようなマーカーを作ることができます!

marker01

マーカーを追加するには

マップ上にマーカーを追加するには GoogleMap#addMarker() メソッドを使います。引数には MarkerOption クラスのインスタンスを渡し、 MarkerOption でマーカーのタイトルやスニペットなどを設定します。

// マーカーを貼る緯度・経度
LatLng location = new LatLng(35.697261, 139.774728);

// マーカーの設定
MarkerOptions options = new MarkerOptions();
options.position(location);
options.title("クラスメソッド株式会社");
options.snippet(location.toString());

// マップにマーカーを追加
mMarker = mMap.addMarker(options);

marker02

アイコンをカスタマイズする

マップ上に表示されるアイコンをカスタマイズしてみます。アイコンを変更するには MarkerOption#icon() メソッドを使います。引数には BitmapDescriptor という GoogleMap ライブラリ独自の Bitmap クラスを使います。BitmapDescriptorFactory クラスを使って BitmapDescriptor を生成することができ、 マーカー用のアイコンを作る defaultMarker() メソッドを使うと色違いのデフォルトアイコンを作れたりします。以下では BitmapDescriptorFactory#defaultMarker() を使い、青色のデフォルトアイコンを設定してみました。

// 紫色のアイコン
BitmapDescriptor icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE);
options.icon(icon);

marker03

defaultMarker() で作成できるデフォルトアイコンのカラーバリエーションは下図の通りです。

marker04

ちなみに defaultMarker() で渡す引数は float 型で Hue (色相) を直接指定することもできました。RED の 0f から始まり 360f まで自由に指定できます。 180f は RED の補色である CYAN になります。
また BitmapDescriptor#fromResource() を使うとリソース内の Drawable をアイコンに指定できます。

BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.ic_logo);
options.icon(icon);

marker05

InfoWindow をカスタマイズする

次に InfoWindow (マーカーをタップしたときに表示されるウインドウ) をカスタマイズしてみます。InfoWindow の表示を制御するには、まず InfoWindowAdapter インターフェースを実装したカスタムアダプタークラスを作ります。InfoWindowAdapter を実装すると以下のようになるはずです。

private class CustomInfoAdapter implements InfoWindowAdapter {

    public CustomInfoAdapter() {
    }

    @Override
    public View getInfoWindow(Marker marker) {
        return null;
    }

    @Override
    public View getInfoContents(Marker marker) {
        return null;
    }

}

実装メソッド getInfoWindow() と getContents() の中でカスタムViewの生成を行います。つまり View であれば何でも良い!ということになります。自由にカスタマイズできますね。getInfoWindow() と getContents() メソッドでは Marker インスタンスが渡されるので、そこでどの Marker がタップされたか判別し、プロパティの情報を View に反映します (今回は getInfoWindow() のときのみで実装しました)。

/**
 * InfoAdapter のカスタムクラス.
 */
private class CustomInfoAdapter implements InfoWindowAdapter {

    /** Window の View. */
    private final View mWindow;

    /**
     * コンストラクタ.
     */
    public CustomInfoAdapter() {
        mWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null);
    }

    @Override
    public View getInfoWindow(Marker marker) {
        render(marker, mWindow);
        return mWindow;
    }

    @Override
    public View getInfoContents(Marker marker) {
        return null;
    }

    /**
     * InfoWindow を表示する.
     * @param marker {@link Marker}
     * @param view {@link View}
     */
    private void render(Marker marker, View view) {
        // ここでどの Marker がタップされたか判別する
        if (marker.equals(mMarker)) {
            // 画像
            ImageView badge = (ImageView) view.findViewById(R.id.badge);
            badge.setImageResource(R.drawable.ic_logo);
        }
        TextView title = (TextView) view.findViewById(R.id.title);
        TextView snippet = (TextView) view.findViewById(R.id.snippet);
        title.setText(marker.getTitle());
        snippet.setText(marker.getSnippet());
    }

}

あとはこのカスタムアダプターをインスタンス化し、 GoogleMap#setInfoWindowAdapter() でセットして完成です!

mMap.setInfoWindowAdapter(new CustomInfoAdapter());

marker01

ソースコード

今回のサンプルプロジェクトを github に公開しました!ぜひ Pull してみてください。AndroidManifest.xml の application タグ内の meta-data の value を自身の Google API Key に変更しないと動かないのでご了承ください。詳しくはこちらに載っているのでご参照ください!

suwa-yuki/GoogleMapMarkerSample

まとめ

InfoWindow は View であれば何でも良い、というところが自由度高くて良い感じですね。微妙なところは Adapter の Marker の判別処理ですね。 Marker に Id などセットできれば解消できそうなんですが…。現状は ArrayList など使って自分で作っていくしかなさそうです。

  • Lute

    GitHubで公開されているGoogleMapMarkerSampleですが、.projectファイルが含まれていない為、そのままではeclipseに取り込めませんでした。

    • suwa.yuki

      コメントいただきありがとうございます!
      確かに.projectファイルはリポジトリに追加されていませんでした。
      Eclipseインポート時には自動生成されるため、不要と考えておりましたが
      Eclipseに取り込めない場合があるということで、先ほど更新させていただきました。
      ご指摘ありがとうございました。