最近 Maps SDK for Android でアレコレ試しています。前にも記事を書きましたが、地図を使って色々な表現ができるのは面白いですね。
その中でも、特に活用の幅が広そうな Marker について、この記事と次の記事の2つに分けて書いておこうと思いました。
- 基本機能だけでできること(この記事)
- Utility Library を使ってできること(次の記事)
この記事でやること
- 地図上に Marker を置く
- Marker の角度、動き、色、アイコン画像を変える
- ユーザーからのインタラクションに反応させる
- Info Window をカスタマイズする
注意事項
Android Studio で Maps SDK for Android が使えることを前提にしています。そのための手順は以前まとめましたので、そちらご確認下さい。
環境
このページに書かれているコード等は以下の環境下で動作・検証しています。
macOS Mojave バージョン10.14.6
Android Studio 3.5
Pixel3a + Android 9
また、ここに書いた内容をもとにしたサンプルコードを GitHub で公開しています。
そもそも Marker とは
下の画像がそれで、地図上で任意の位置を示すピンみたいなものです。
タップに反応させて、ピンが示す場所の説明などを出すことができます。このときに表示される小さな吹き出しをInfo Windowと呼びます。これもカスタマイズが可能で、かつユーザーからのインタラクションに反応させることもできます。
地図上に Marker を置く
前の記事でも書きましたが、基本は GoogleMap#addMarker を呼び出して配置します。
Marker の位置やタイトル、詳細などは、addMarker の引数である MarkerOptions にセットしていきます。このとき title や snippet など MarkerOptions のメソッドはメソッドチェーンで書くことができます。上図の Marker なら以下のようなコードになりますね。
addMarker(MarkerOptions()
.title("大阪駅")
.snippet("JR西日本 大阪環状線の駅")
.position(LatLng(34.702485, 135.495951))
.zIndex(2.0f)
Marker の角度、動き、色、アイコン画像を変える
Marker の要素は、それぞれ MarkerOptions の下記メソッドで設定します。
メソッド | 引数の型 | 変更するもの |
---|---|---|
title | String | タイトル。Info Window 内に太字で表示 |
snippet | String | 説明。Info Window 内に表示 |
position | LatLng | Marker を置く場所(緯度・経度) |
alpha | Float | アイコンの透過度。1.0fが不透明 |
rotation | Float | 回転角(時計回り) |
draggable | Boolean | ドラッグ操作の可否 |
flat | Boolean | アイコンのフラット表示の有無 |
icon | BitmapDescriptor | アイコンの画像 |
zIndex | Float | 重なり。大きい方が前面 |
前項と同じ Marker を、これらのメソッドも指定して配置すると以下のようになります。単にデフォルト値をわざわざ指定してるだけなんですが…。
addMarker(MarkerOptions()
.title("大阪駅")
.snippet("JR西日本 大阪環状線の駅")
.position(LatLng(34.702485, 135.495951))
.alpha(1.0f)
.rotation(0.0f)
.draggable(false)
.flat(false)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
.zIndex(2.0f)
角度を変える
rotation に Float 値をセットすると、ピンが時計回りに指定した角度だけ回転します。
rotation(90.0f)
とするとこんな感じ。
動きを変える
Google Map では、地図に2本指を立てて引き起こすことで3D表示にしたり、2本指で地図を回転させたりという操作が可能です。そのときマーカーは地図の動きからは独立しており、立ったままの状態を保ちます。しかし、これを地図の動きと連動させることができます。
flat(true)
とすると以下のように、地図が回転すると一緒に回転し(左)、3D表示になると地面に張り付き(右)ます。
色を変える
icon メソッドを BitmapDescripter オブジェクトを引数にして呼び出すと、アイコンの画像を変更することができます。 BitmapDescripter オブジェクトを生成するには、 BitmapDescripterFactory クラスを使用します。
色を変更するだけなら BitmapDescripterFactory クラスの defaultMarker メソッドと "HUE_" で始まる名前の定数を利用します。例えば緑にするならicon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))
となります。
アイコンの画像を変える
アイコンの画像を変えることもできます。
前項でアイコンの色を変えたのと同じように、BitmapDescripterFactory クラスの fromXXXXX メソッドからの戻り値を MarkerOptions#icon メソッドに指定することで、通常のアイコンが任意のアイコンに変更されます。メソッド名の "XXXXX" は、その画像をどこから取得するかによって変わります。
例えば駅舎の画像が drawable/station_1.png にあるとして、それをアイコンに設定する場合はicon(BitmapDescriptorFactory.fromResource(R.drawable.station_1))
というようになります。
ユーザーからのインタラクションに反応させる
GoogleMap クラスにはいくつものリスナが定義されており、その中に Marker で発生したイベントに対するものがあります。
リスナ | イベント |
---|---|
GoogleMap.OnMarkerClickListener | Marker をクリック |
GoogleMap.OnMarkerDragListener | Marker をドラッグもしくはドロップ |
GoogleMap.OnInfoWindowClickListener | Info Window をクリック |
上記は主なもので、他にも複数のリスナがあります。API リファレンスでご確認ください。
なお、リスナは Marker 自身にではなく GoogleMap に対して設定する ことに注意してください。
以下は、Marker をどこからどこまでドラッグ&ドロップしたのか、ドロップしたタイミングで Snackbar に表示しています。
googleMap.setOnMarkerDragListener(object : GoogleMap.OnMarkerDragListener {
private var start: LatLng? = null
private var end: LatLng? = null
override fun onMarkerDragStart(marker: Marker) {
marker.position.let { start = LatLng(it.latitude, it.longitude) }
}
override fun onMarkerDrag(marker: Marker) {
// Do Nothing.
}
override fun onMarkerDragEnd(marker: Marker) {
marker.position.let {
end = LatLng(it.latitude, it.longitude)
if (start != null && end != null) {
val message = String.format("(%.4f, %.4f) -> (%.4f, %.4f)", start!!.latitude, start!!.longitude, end!!.latitude, end!!.longitude)
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show()
}
}
}
})
Info Window をカスタマイズする
Marker をタップすると表示される Info Window もカスタマイズすることができます。例えば、以下のような具合です。
GoogleMap.InfoWindowAdapter を使います。ここにどのようなレイアウトにしたいかを定義して、そのアダプタを GoogleMap#setInfoWindowAdapter でセットします。
上記の Info Window を表示するアダプタのコードは以下の通りです(レイアウトのコードは省略しています)。
class StationInfoWindowAdapter(private val context: Context) : GoogleMap.InfoWindowAdapter {
override fun getInfoContents(marker: Marker): View? = null
override fun getInfoWindow(marker: Marker): View? = setupWindow(marker)
private fun setupWindow(marker: Marker): View =
LayoutInflater.from(context).inflate(R.layout.info_window_station, null, false).apply {
val station = marker.tag as Station
findViewById<ImageView>(R.id.imageView).setImageResource(station.imageResId)
findViewById<TextView>(R.id.textTitle).text = marker.title
findViewById<TextView>(R.id.textSnippet).text = marker.snippet
}
}
GoogleMap.InfoWindowAdapter では以下のメソッドを実装して View オブジェクトを返す必要があります。
メソッド | 戻り値 |
---|---|
getInfoWindow(Marker marker) | Info Window 自体の View |
getInfoContents(Marker marker) | Info Window の中に表示される View |
例えば上述のコードで getInfoWindow と getInfoContents の戻り値を逆にすると、以下のようになります。分かりにくいですが Info Window の中にパディングができており、レイアウトが入り込んでいます。
Info Window に適用される View をどのメソッドから得るかは、以下のように判断されます。
- getInfoWindow が View を返す場合は getInfoWindow
- getInfoWindow が null が返し、 getInfoContents が View を返す場合は getInfoContents
- getInfoWindow と getInfoContents の両方とも null を返す場合はデフォルトの View
まとめ
Maps SDK for Android に標準で用意されている手段で Marker や Info Window をカスタマイズする方法についてまとめてみました。
追加コンポーネントである Google Maps Android API Utility Library を使用すると、更に Marker でできることが広がります。
次の記事では Utility Library を使用して Marker をカスタマイズする方法を書きたいと思います。