[Maps SDK for Android] 地図にMarkerを置く(その2・Utility Library編)
前に書いた記事で、Maps SDK for Android で表示した地図に配置する Marker について書きました。基本機能だけでも色々できますが、Google Maps Android API Utility Library を導入すると更にできることが広がります。
ここでは Utility Library の導入と使用法についてまとめてみました。
- 基本機能だけでできること(前の記事)
- Utility Library を使ってできること(この記事)
この記事でやること
- Utility Library の導入
- Marker をクラスタにまとめる
- クラスタ化した Marker をカスタマイズする
- Icon Generator を使って地図に地名を表示する
注意事項
Android Studio で Maps SDK for Android が使えることを前提にしています。そのための手順は以前まとめましたので、そちらご確認下さい。
環境
このページに書かれているコード等は以下の環境下で動作・検証しています。
macOS Mojave バージョン10.14.6
Android Studio 3.5
Pixel3a + Android 9
また、ここに書いた内容をもとにしたサンプルコードを GitHub で公開しています。
Utility Library の導入
モジュールの build.gradle に以下の dependencies を追加します。
dependencies {
compile 'com.google.maps.android:android-maps-utils:0.5+'
}
Marker をクラスタにまとめる
地図のズームレベルが下がって複数の Marker が一箇所に集まったとき、「クラスタ」にまとまるようにすることができます。
下の図だと、左が全ての Marker がバラバラに表示されている状態、右がクラスタになって表示されている状態です。
以下の手順で実装することができます。
- ClusterItem を実装したクラスを作成
- ClusterManager オブジェクトを生成
- ClusterItem を ClusterManager に追加
ClusterItem を実装したクラスを作成
Marker ひとつひとつに相当するクラスです。 com.google.maps.android.clustering.ClusterItem を実装して、必要なメソッドを定義すればOKです。
getSnippet / getTitle / getPosition では、それぞれ Marker の snippet / titie / position に相当するものを返すようにします。以下は実装例です。
class SegmentClusterItem(val segment: Segment) : ClusterItem {
override fun getSnippet(): String = segment.flowerName
override fun getTitle(): String = segment.title
override fun getPosition(): LatLng = segment.coordinate
}
ClusterManager オブジェクトを生成
com.google.maps.android.clustering.ClusterManager クラスをインスタンス化します。
このとき GoogleMap の以下のメソッドに ClusterManager インスタンスを設定するようにします。
- setOnCameraIdleListener
- setOnMarkerClickListener
以下は ClusterManager をインスタンス化しているところです。
val manager = ClusterManager<SegmentClusterItem>(this@ClusteringActivity, this).apply {
setOnCameraIdleListener(this)
setOnMarkerClickListener(this)
}
ClusterItem を ClusterManager に追加
あとは ClusterManager#addItem で ClusterManager に ClusterItem を追加していくだけです。これで Marker がある程度まで接近するとクラスタにまとまるようになります。
Segment.values().forEach {
manager.addItem(SegmentClusterItem(it))
}
クラスタにならない場合
上記の実装をしても Marker がクラスタにまとまらない場合は、Marker の数が少ないことが考えられます。
Marker がクラスタにまとまるかどうかは、一定の領域内に含まれる Marker(=ClusterItem)の数で判定されます。デフォルトでは、5個以上の Marker が領域内に含まれないとクラスタになってくれません。これを変更するには、クラスタにする Marker の数を定義した ClusterRenderer オブジェクトを ClusterManager#setRenderer にセットする必要があります。
以下は、DefaultClusterRenderer を継承した ClusterRenderer クラスの実装例です。shouldRenderAsCluster で「何個以上の Marker が集まったらクラスタにするか」を判定します。この例では、2つ以上でクラスタになるようにしています。
class SegmentClusterRenderer(context: Context, map: GoogleMap, manager: ClusterManager<SegmentClusterItem>) : DefaultClusterRenderer<SegmentClusterItem>(context, map, manager) {
override fun shouldRenderAsCluster(cluster: Cluster<SegmentClusterItem>?): Boolean {
// ClusterItemが一定距離内にいくつ集まったらクラスタ化するかをBooleanで返す
return cluster?.size ?: 0 >= 2
}
}
以下でこの Renderer を Manager にセットしています。
manager.renderer = SegmentClusterRenderer(this@ClusteringActivity, map, manager)
クラスタ化した Marker をカスタマイズする
ClusterRenderer と IconGenerator を併用することで、Marker のアイコンやクラスタのアイコンを変更することができます。
下の図は、大阪市の各区役所に Marker を立てて、区の花をアイコンで表示しています。クラスタになると、そこに含まれる Marker の数が女性のイラストに表示されるようにしています。
ClusterRenderer で継承すべきメソッドは以下の通りです。
両方とも実装すべき処理は同じで、Marker やクラスタが描画されるときの MarkerOptions を変更することになります。
メソッド | 実装内容 |
---|---|
onBeforeClusterItemRendered | Marker(=ClusterItem) のカスタマイズ |
onBeforeClusterRendered | クラスタのカスタマイズ |
また com.google.maps.android.ui.IconGenerator を使えば任意のレイアウトで Bitmap を生成することが可能です。生成した Bitmap を、BitmapDescriptorFactory#fromBitmap を使って MarkerOptions#icon にセットすることで、Marker やクラスタのアイコンを任意に変更しています。
以下は DefaultClusterRenderer を継承した ClusterRenderer の例で、 res/layout/icon_segment.xml のレイアウトを使って Marker にアイコンを設定しています。icon_segment.xml には imageIcon というIDが付与された ImageView だけが配置されています。
private val itemImageView: ImageView
private val itemIconGenerator: IconGenerator = IconGenerator(context).apply {
val iconView = LayoutInflater.from(context).inflate(R.layout.icon_segment, null, false).apply {
itemImageView = findViewById(R.id.imageIcon)
}
setContentView(iconView)
}
override fun onBeforeClusterItemRendered(item: SegmentClusterItem, markerOptions: MarkerOptions) {
itemImageView.setImageResource(item.segment.imageResId)
val icon = itemIconGenerator.makeIcon()
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon))
}
ざっくり以下のような手順です。
- レイアウトから View をインフレート
- インフレートした View から、操作したい View を findViewById で取得
- IconGenerator#setContentView で、1の View をセット
- 2に任意の文字や画像をセット
- IconGenerator#makeIcon で Bitmap を生成
- MarkerOptions#icon に生成した Bitmap をセット
Icon Generator を使って地図に地名を表示する
地図上に地名などを表示したいという要件もあると思いますが、Marker に title や snippet をセットするだけでは Info Window のコンテンツとしてしか表示できません。
なのでここでは IconGenerator を使って、マーカーとして地名を強引に表示してみました。
IconGenerator で文字画像を作成するのは、前項と同じやり方で可能です。TextView を含んだレイアウト(もしくは TextView そのもの)を IconGenerator のコンテンツとして設定し、任意の文字列を TextView にセットしてから IconGenerator#makeIcon するという手順になります。
これで Bitmap ができますので、あとは Marker を生成して icon にセットするということになります。
val textView: TextView
val iconGenerator = IconGenerator(this@CustomIconActivity).apply {
setBackground(null) // nullにしないとデフォルトの背景が表示されてしまう(文字だけにならない)
textView = layoutInflater.inflate(R.layout.icon_station_name, null, false) as TextView
setContentView(textView)
}
textView.text = station.title
addMarker(MarkerOptions()
.icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon()))
.position(station.position)
.anchor(0.5f, 0.0f)
.zIndex(2.1f))
まとめ
以上、Utility Library を使った Marker のカスタマイズについてまとめてみました。
Utility Library には KML を扱ったりヒートマップを追加したりと、他にも多彩な機能が用意されています。
「参考」に公式ドキュメントへのリンクを貼っておきましたので、実際に読んで色々触ってみるのをお勧めします。この記事がそのきっかけになれば、大変嬉しく思います。
参考
Google Maps Android API Utility Library
googlemaps/android-maps-utils (GitHub)
Author And Source
この問題について([Maps SDK for Android] 地図にMarkerを置く(その2・Utility Library編)), 我々は、より多くの情報をここで見つけました https://qiita.com/outerlet/items/8e677fb8fad9f9a86b99著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .