Google Map V3 ベータ版は非推奨!現行バージョンを使おう!

Google: As of the date of this release note, the Maps SDK for Android Beta (versions 3.0.0 and 3.1.0) client library is deprecated.

V3ベータ版の打ち切りがアナウンスされましたので、これからGoogle Mapを導入される方は現行バージョンをお使い下さい。

APIキーを設定

新規に作成したグーグルマッププロジェクトを参考にしながら、ドキュメントを読むと分かりやすいです。クラウドコンソール上で作ったAPIキーをlocal.propertiesに設定して下さい。プラグインで秘匿化されます。以前のようにデバッグ用とリリース用を分ける必要はありません。

SHA-1証明書のフィンガープリントについて

APIキーの不正利用を防ぐために、クラウドコンソール上でパッケージ名とフィンガープリントを設定できます。デバッグ用はお使いのパソコンによって値が異なり、リリース用はアプリによって値が決まります。

デバッグ用のフィンガープリントを調べる

デバッグ用の値は、Android Studioの右上にあるGradleボタンを押して、像アイコンを押して表示されるウィンドウからgradle signingReportと打って実行すると出力されます。Android Studioを再インストールした場合には値が変わりますのでご注意を。

build.gradle

関連するユーティリティもGoogleが開発しており、クラスタリングや測地線、分布図、GeoJSONなどに対応しています。

// https://mvnrepository.com/artifact/com.google.android.gms/play-services-maps
implementation 'com.google.android.gms:play-services-maps:17.0.1'

// https://github.com/googlemaps/android-maps-utils
implementation 'com.google.maps.android:android-maps-utils:2.2.6'

レイアウト

マップのフラグメントはコンテナビューにしておくと『完了するまでは非表示にしておいて欲しい』といった要望にも応えやすく何かと便利です。(非表示のままマップを読み込むには透明度を0にする必要があり、fragmentにはalphaがない。)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
            android:id="@+id/map_fragment_container_view"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</FrameLayout>

Google Mapをセットアップ

class MapActivity : AppCompatActivity(R.layout.activity_map), OnMapReadyCallback, GoogleMap.OnMapLoadedCallback {
    
    private val mapFragment: SupportMapFragment get() = supportFragmentManager.findFragmentById(R.id.map_fragment_container_view) as SupportMapFragment
    
    private fun loadMap() {
        mapFragment.getMapAsync(this)
    }

    override fun onMapReady(map: GoogleMap) {
        map.setOnMapLoadedCallback(this)
    }

    override fun onMapLoaded() {
        // Completion
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        loadMap()
    }
}

上記はコールバックを実装するサンプルですが、以下のようにブロックで簡潔に書けるようになっています。これでマップが表示されます。

private fun loadMap() {
    mapFragment.getMapAsync {
        it.setOnMapLoadedCallback { onMapLoaded(it) }
    }
}

private fun onMapLoaded(map: GoogleMap) {
    // Completion
}

ピンのクリックの検出

map.setOnMarkerClickListenerを使うと警告が出るようになっています。

Using this method may override behaviors set by the Maps SDK for Android Utility Library. If you are not using clustering, GeoJson, or KML, you can safely suppress this warning, otherwise, refer to the utility library's migration guide: https://bit.ly/3kTpQmY

クラスタリングする場合は、ClusterManagerだけにリスナーを設定しましょう。ピンの上部に表示されるInfoWindowも同様です。

class CustomClusterItem(private val marker: MarkerOptions): ClusterItem {

    override fun getTitle(): String? = marker.title
    override fun getSnippet(): String? = marker.snippet
    override fun getPosition(): LatLng = marker.position
}
val clusterManager: ClusterManager<CustomClusterItem> = ClusterManager(this, map)
clusterManager.setOnClusterItemClickListener { false }
clusterManager.setOnClusterItemInfoWindowClickListener {  }

クラスタリングする場合のピン立て

ピンは、map.addMarker(markerOptions)を使わずに、ClusterManagerにアイテムをセットします。あとでcluster()を呼ばないとピンが表示されません。

val position = LatLng(35.681236, 139.767125)
val item = CustomClusterItem(
    MarkerOptions()
        .title("東京")
        .snippet(position.toString())
        .position(position)
)
clusterManager.addItem(item)

map.setOnCameraMoveListener { clusterManager.cluster() }
map.moveCamera(position)

クラスタリングする場合のピンのカスタマイズ

clusterManager.rendererをDefaultClusterRendererのカスタムクラスに変える必要があります。

ピンを写真などに置き換えるのは簡単ですが、既存のピンにテキストを追加するのは無理そう。そうゆう場合は、ピンの画像を自前で用意してテキストを合成する方法になると思います。