LoginSignup
1
1

[備忘録] Androidアプリに Google Map を追加する

Posted at

前提

これをやったんだ

大まかな手順

  1. Google Maps Platfrom にて API KeyMap ID を設定する
  2. Viewファイルを作成
  3. 処理部分の記述

Google Maps Platform にて API KeyMap ID を設定する

API Keyの設定

こちらを参考に設定した。

API Key をViewファイルなどにハードコードで書き込むとセキュリティ上問題があるため、今回はAndroidプロジェクト内に存在するlocal.propertiesというファイルに書き込んた。

Map IDの設定

Map ID を用いると地図のスタイルを変更することができる。
ちなみに 2023/08/18 時点で Android の Map ID はまだサポートされていないため、別に意味はない

こちらを参考に設定した。
https://developers.google.com/maps/documentation/get-map-id?hl=ja

local.propertiesとは

デフォルトで.gitignoreされているのでAPI Keyなど公開したくない定数を定義できるファイル。
Cloneする度に設定する必要があるので注意。
またGitHub Actionsを利用する際にもsecretsで別途対応する必要があるため注意。

今回はこんな感じで設定する

GOOGLE_MAPS_API_KEY=****************************
GOOGLE_MAP_ID=*********

以下参考

以下の項目など、ビルドシステムのローカル環境プロパティを構成します。

  • ndk.dir - NDK へのパス。このプロパティは非推奨になりました。ダウンロードしたバージョンの NDK はすべて、Android SDK ディレクトリ内の ndk ディレクトリにインストールされています。
  • sdk.dir - SDK へのパス。
  • cmake.dir - CMake へのパス。
  • ndk.symlinkdir - Android Studio 3.5 以降では、インストールされている NDK のパスよりも短くできる、NDK へのシンボリック リンクが作成されます。

Viewファイルを作成

2つのViewファイルを作成した。

  • activity_main.xml
    • Google Mapを表示するメイン画面
  • marker_info_contents.xml
    • マーカーをタップした際に表示されるポップ!
ファイル名

activity_main.xml

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

    <fragment
        android:id="@+id/map_fragment"
        class="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        map:mapId="${GOOGLE_MAP_ID}" />

</FrameLayout>

コードの解説

  • xmlns:map="http://schemas.android.com/apk/res-auto"
    maps のカスタム XML 属性を使用するための宣言。

  • class="com.google.android.gms.maps.SupportMapFragment"
    必要なライフサイクルのニーズを自動的に処理する、マップのViewのWrapper。
    こいつを使うと魔法のように簡単に Google Map を表示できる。
    詳細はこちら。

marker_info_contents.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:padding="8dp">

    <TextView
        android:id="@+id/text_view_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        android:textStyle="bold"
        tools:text="Title"/>

    <TextView
        android:id="@+id/text_view_address"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        tools:text="123 Main Street"/>

    <TextView
        android:id="@+id/text_view_rating"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        tools:text="Rating: 3"/>

</LinearLayout>

特殊なことは特にない。

処理部分の記述

構成はこんな感じだった。

app/src/main/java/com/google/codelabs/buildyourfirstmap
├── BitmapHelper.kt
├── MainActivity.kt
├── MarkerInfoWindowAdapter.kt
└── place
    ├── Place.kt
    ├── PlaceRenderer.kt
    ├── PlacesReader.kt
    └── PlacesResponse.kt

各ファイルの概要

名前 詳細
MainActivity.kt メインアクティビティー 
BitmapHelper.kt Drawableファイルをマーカーアイコンとして使用するために、BitmapDescriptor に変換するためのヘルパー 
MarkerInfoWindowAdapter マーカーをタップした際のウィンドウに関する処理をする
名前 詳細
Place.kt 地図上ある一点の情報に関するDataClass。マーカーのDataClassだと思って良い
PlacesReader.kt ファイル places.json から場所 JSON オブジェクトのリストを読み取る。places.jsonは用意されたもので、マーカーとして表示したい場所が記述されている。
PlaceRenderer.kt Place オブジェクト用のカスタムClusterRenderer。マーカーに関する処理を記述する
PlacesResponse.kt places.json からデータを受け取るためのDataClass

各ファイルの詳細

MainActivity.kt

....

class MainActivity : AppCompatActivity() {
    
    // 場所(マーカー)のリスト
    private val places: List<Place> by lazy {
        PlacesReader(this).read()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val mapFragment =
            supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
        lifecycleScope.launchWhenCreated {
            val googleMap = mapFragment.awaitMap()  // 地図を取得する
            googleMap.awaitMapLoad()  // マップの読み込みが完了するまで待機
            val bounds = LatLngBounds.builder()  // 位置座標の境界を作成
                                                 // LatLngは位置座標に関するクラス
            places.forEach { bounds.include(it.latLng) }
            googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))

            addClusteredMarkers(googleMap)
        }
    }

    private var circle: Circle? = null

    /**
     * 指定された[item]の周囲に[circle]を追加します
     */
    private fun addCircle(googleMap: GoogleMap, item: Place) {
        circle?.remove()
        circle = googleMap.addCircle {
            center(item.latLng)
            radius(1000.0)
            fillColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimaryTranslucent))
            strokeColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimary))
        }
    }

    /**
     * クラスタリングのサポートを使用してマップにマーカーを追加します。
     */
    private fun addClusteredMarkers(googleMap: GoogleMap) {
        // ClusterManager クラスを作成し、カスタム レンダラを設定します。
        val clusterManager = ClusterManager<Place>(this, googleMap)
        clusterManager.renderer = PlaceRenderer(
            this, googleMap, clusterManager
        )

        // カスタム情報ウィンドウ アダプターを設定します
        clusterManager.markerCollection.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))

        // 場所を ClusterManager に追加します。
        clusterManager.addItems(places)
        clusterManager.cluster()

        // ClusterManager を OnCameraIdleListener として設定します。
        // ズームインまたはズームアウトすると再クラスタリングが可能。
        googleMap.setOnCameraIdleListener {
            clusterManager.onCameraIdle()
        }

        // itemの周りに円を表示
        clusterManager.setOnClusterItemClickListener { item ->
            addCircle(googleMap, item)
            return@setOnClusterItemClickListener false
        }

        //
        // カメラ移動時の透明度の変更について
        //
        googleMap.setOnCameraMoveStartedListener {
            // カメラが動き始めたら、マーカーのアルファ値を半透明に変更します。
            clusterManager.markerCollection.markers.forEach { it.alpha = 0.3f }
            clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 0.3f }
        }

        googleMap.setOnCameraIdleListener {
            // カメラの動きが停止したら、アルファ値を不透明に戻します。
            clusterManager.markerCollection.markers.forEach { it.alpha = 1.0f }
            clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 1.0f }

            // カメラの動きが停止したときに再クラスタリングを実行できるように、
            // カメラの動きが停止したときにclusterManager.onCameraIdle()を呼び出します。
            clusterManager.onCameraIdle()
        }
    }
}

エミュレーターでネットが使えない

自分はこちらが原因だった。

MacのDNSに8.8.8.8を指定した。

マップインフォアダプター

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1