[Maps SDK for Android] MapViewを配置したFragmentを作成する

# この記事でやること

以前、[Maps SDK for Android を使って Android アプリに地図を埋め込む方法](https://qiita.com/outerlet/items/0d74061fa5d625a33dfb)について書きました。その時に使ったのは [SupportMapFragment](https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/SupportMapFragment) で、地図を出すなら一番簡単だとして公式も紹介している方法です。
ですが、既存のものでなく自分でカスタマイズした Activity や Fragment に地図を表示したいという要求もあると思います。
前の記事を書いた時はその方法が分かってなかったのですが、色々調べてできるようになりましたので、備忘録ついでに共有しておこうと思います。

# 注意事項

この記事では Android Studio で Maps SDK for Android が使える状態であることを前提にしています。
Maps SDK for Android を利用する際は Google Cloud Platform Console にログインして API キーを取得したり、それを Android Studio プロジェクトに設定するなどの作業が必要です。このあたりの手順は [前回の記事](https://qiita.com/outerlet/items/0d74061fa5d625a33dfb) にまとめましたので、そちらご確認下さい。

# 環境

このページに書かれているコード等は以下の環境下で動作・検証しています。

macOS Mojave バージョン10.14.6
Android Studio 3.4.2
Pixel3a + Android 9

# 組み込み手順

### 1. レイアウトの作成

Fragment 用のレイアウトを作成し MapView を配置します。
私が作成したのは以下のようなものです。

```xml
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/container">

    <com.google.android.gms.maps.MapView
            android:id="@+id/mapView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />

</androidx.constraintlayout.widget.ConstraintLayout>
```

### 2. Fragment の作成

Activity や Fragment に配置した MapView に地図を表示するには、以下のライフサイクルメソッドを全てオーバーライドして、各メソッドの中でそれぞれに対応した MapView のメソッドを呼ぶ必要があります（だいたい同じ名前のメソッドです）。

|Activity|Fragment|MapView|
|:--|:--|:--|
|onCreate(Bundle)|※以下参照|onCreate(Bundle)|
|onStart()|onStart()|onStart()|
|onResume()|onResume()|onResume()|
|onPause()|onPause()|onPause()|
|onStop()|onStop()|onStop()|
|onDestroy()|onDestroy()|onDestroy()|
|onSaveInstanceState(Bundle)|onSaveInstanceState(Bundle)|onSaveInstanceState(Bundle)|
|onLowMemory()|onLowMemory()|onLowMemory()|

例えば、以下のような具合です。"mMapView" は MapView のインスタンスです。

```kotlin
override fun onStart() {
    super.onStart()
    mMapView.onStart()
}
```

また Fragment の場合、onCreate では MapView のインスタンスを拾えません。そのため、以下のどちらかで MapView#onCreate を呼ぶことになります。
Kotlin Android Extensions を使うなら onViewCreated になります。

* onCreateView(LayoutInflater, ViewGroup?, Bundle?): View?
* onViewCreated(View, Bundle?)

こちらの例は以下です。ついでに getMapAsync も呼び出してしまっています（後述）。

```kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    mMapView = mapView.apply {
        onCreate(savedInstanceState)
        getMapAsync(this@CustomMapFragment)
    }
}
```

### 3. コールバックの実装と getMapAsync の呼び出し

ここまで済めば、以下の手順で完了です。

1. OnMapReadyCallback インターフェイスを実装したクラスを用意
2. MapView#getMapAsync を実行。 OnMapReadyCallback#onMapReady(GoogleMap) が呼び出されて、利用できる状態の GoogleMap オブジェクトを得ることができます。

#### （補足）MapsInitializer について

[MapsInitializer](https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/MapsInitializer) というクラスがあります。 Maps SDK for Android を初期化するクラスで、初期化をしないと地図上で画像の描画やカメラの位置調整ができないとのことです。
MapView#getMapAsync で GoogleMap オブジェクトを得る前に MapsInitializer#initialize(Context) を実行せよとあるのですが、少なくとも私の環境ではこれを実行しなくても問題なく GoogleMap オブジェクトが得られました。API ドキュメントにも『 GoogleMap オブジェクトが得られたなら気にする必要はない』などと書かれているので、利用する状況がいまいちよく分かっていません。
分かり次第追記しますが、そのあたりの事情をお分かりの方いれば、お教え頂けると嬉しいです。

# まとめ

MapView を使って地図を実装する方法について書いてみました。
Activity では SupportMapFragment を使えばいいので、あまり利用価値がないかも知れません。ただ自作の Fragment を使えることにメリットはあると思うので、機会があれば活用していきたいと思います。