🚗

Navigation SDK for AndroidのDrop-In UI

2023/06/26に公開

はじめに

この記事ではMapbox Navigation SDK for Android v2におけるDrop-In UIの使い方について紹介します。

Drop-In UIとは

Drop-In UIとは、ナビゲーションに関する機能が全て詰め込まれたView機能のことです。アプリケーション側ではそのViewを読み込むだけで経路探索、経路表示、ターン・バイ・ターンナビゲーション等ナビゲーションに必要なすべての機能を使用できます。

ちなみに、以前こちらの記事で以下のように記述していました。

例えば、v1ではViewを継承したNavigationViewが実装されていました。これを使うことで、全部入りのナビゲーションはこのサンプルのように少ない記述量で作成することができました。これに対し、v2ではNavigationViewが提供されません。代わりに、ナビゲーションのための各パーツが提供されており、自身でそれらを組み合わせて使います。v1におけるこのサンプルがイメージとしては近いです。

この後v2.4.0でDrop-In UIが実装されたにも関わらず、記事のアップデートを行っておりませんでした。そこで、少し遅くなりましたが今回はDrop-In UI (NavigationView)の使い方について見ていきます。

Drop-In UIに関する詳細は以下のドキュメントをご参照ください。

https://docs.mapbox.com/android/navigation/guides/drop-in-ui/

v2におけるDrop-In UIの特徴

v0, v1のNavigationViewでは経路探索に関するUIが含まれておらず、自分で実装する必要がありました。それに対しv2のNavigationViewは経路探索のUIも実装されているため、特にカスタマイズを行わない場合にはKotlinのコードを書く必要すらありません。

Drop-In UIのサンプルコード

通常、SDKの使い方はSDKに同梱されているサンプルコードが一番参考になります。しかし、Drop−In UIに関してはSDKにサンプルが同梱されておりません。かわりに、以下のExampleのプロジェクトにサンプルがたくさんあります。

https://github.com/mapbox/mapbox-navigation-android-examples

注意事項

テストをする際には課金の発生にご注意ください。Navigation SDK v2はMAU課金とTrip課金の両方が適用されます。MAU課金は100デバイスまで無料です。Trip課金は主にナビゲーション回数に対する課金で1000回までは無料です。特にTrip課金の方は複数の端末で何度もナビゲーションを行うと到達しうる回数なのでご注意ください。詳細はこちらのサイトをご参照ください

ちなみに、Navigation SDK v1はMAU課金のみで、10デバイスまで無料です。

最も簡単なケース

準備

ドキュメントサイトのInstallationの指示通りに設定を行います。

https://docs.mapbox.com/android/navigation/guides/get-started/install/

具体的には以下の点を設定しておくと良いでしょう。

  • シークレットトークンの設定
  • パブリックトークンの設定
  • 依存関係の設定

コード入手先

ここで作成したサンプルコードは以下から入手可能です。

https://github.com/OttyLab/Zenn/tree/main/code/articles/11d9904255c117/RawDropinUi

手元で試す際にはRawDropinUi/app/src/main/res/values/strings.xmlYOUR_MAPBOX_ACCESS_TOKENの部分をご自身のパブリックトークンに変更してください。

レイアウト

以下のコンポーネントをactivity_main.xmlに記述します。

<com.mapbox.navigation.dropin.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigationView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:accessToken="@string/mapbox_access_token"
    xmlns:app="http://schemas.android.com/apk/res-auto">
</com.mapbox.navigation.dropin.NavigationView>

結果

実行すると以下のように経路探索からターン・バイ・ターンナビゲーションまで使用できることが確認できます。

https://www.youtube.com/shorts/lU-63y3ZuSI

最も単純なケースではKotlinのコードを書く必要がありません。ある意味、ノーコードですね。

Route Replay (シミュレーション)を使用する

開発途中のデバッグでは、経路を設定した際にその経路に沿って自動的に自車位置を順次移動させたいと思うかもしれません。そんなときに便利なのがRoute Replay機能です。この機能をONにすると、経路に沿ったGPS位置がNavigation SDK内部で順次作成され、それに応じて自車位置が移動します。シミュレーションモード的なイメージです。

使用するにはonCreateメソッドに以下を追加してください。

val navigationView: NavigationView = findViewById(R.id.navigationView)
navigationView.api.routeReplayEnabled(true)

ただし、v2.12.0〜v2.13.2ではRoute Replayが正常に機能しません。2023年6月25日現在で最新のv2.14.0を使用してください。

カスタマイズする

NavigationViewは地図のスタイルのみならずナビゲーションで表示しているUIも柔軟にカスタマイズ可能です。

コード入手先

ここで作成したサンプルコードは以下から入手可能です。

https://github.com/OttyLab/Zenn/tree/main/code/articles/11d9904255c117/CustomizedDropinUi

手元で試す際にはCustomizedDropinUi/app/src/main/res/values/strings.xmlYOUR_MAPBOX_ACCESS_TOKENの部分をご自身のパブリックトークンに変更してください。

レイアウト

レイアウトは「最も簡単なコード」と同じでOKです。

コード

地図のスタイルのカスタマイズ

地図のスタイルに関する変更はcustomizeViewOptionsで行います。ここでは地図のスタイルを「Mapbox Satellite Streets」、経路の色を以下に変更しています。

  • 渋滞情報がない場合はマゼンタ
  • 渋滞が少ない場合はシアン
  • Casing(経路の外側の線)は赤
binding.navigationView.customizeViewOptions {
    mapStyleUriDay = Style.SATELLITE_STREETS
    routeLineOptions = MapboxRouteLineOptions.Builder(applicationContext)
        .withRouteLineResources(
            RouteLineResources.Builder()
                .routeLineColorResources(
                    RouteLineColorResources.Builder()
                        .routeLowCongestionColor(Color.CYAN)
                        .routeUnknownCongestionColor(Color.MAGENTA)
                        .routeCasingColor(Color.RED)
                        .build()
                )
                .build()
        )
        .build()
}

ナビゲーションで表示しているUIのカスタマイズ

ナビゲーションで表示しているUIの色やアイコンの変更はcustomizeViewStylesで行います。ここでは「ナビゲーションを開始するボタン」のアイコンを変更しています。具体的には以下の場所です。

start button

まずdrawable以下にアイコンファイルを保存します。ここでは以下のICOOON MONO様のラーメンのアイコン(ramen.png)を使用しました。

ramen

CustomizedDropinUi/app/src/main/res/values/styles.xmlで以下のようにアイコンを設定します.

<style name="CustomStartNavigationButtonStyle">
    <item name="extendableButtonIcon">@drawable/ramen</item>
</style>

次に以下のようにスタイルを設定します。

binding.navigationView.customizeViewStyles {
    startNavigationButtonStyle = R.style.MyStartNavigationButtonStyle
}

独自のUIをナビゲーションに使用する

独自のUIをナビゲーションに使用するにはcustomizeViewBindersを使用します。ここでは進捗を示す下側に出ているUIを変更していきます。

progress

まず、表示したいUIのレイアウトを作成します。ここではcustom_trip_progress.xmlというファイルを作成し、以下のように定義しました。中身はTextView一つだけです。

<?xml version="1.0" encoding="utf-8"?>
<merge 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="wrap_content"
    tools:parentTag="android.widget.FrameLayout">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/tripProgressView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingEnd="8dp"
        android:paddingStart="8dp">

        <TextView
            android:id="@+id/messageTextView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="32sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="message here" />


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

次にこのレイアウトを親UIに紐付けるクラスを作成します。

class CustomTripProgressViewBinder : UIBinder {
    override fun bind(viewGroup: ViewGroup): MapboxNavigationObserver {
        val scene = Scene.getSceneForLayout(
            viewGroup,
            R.layout.custom_trip_progress,
            viewGroup.context
        )
        TransitionManager.go(scene, Fade())

        val binding = CustomTripProgressBinding.bind(viewGroup)
        return CustomTripProgressComponent(binding)
    }
}

さらに、CustomTripProgressViewBinderの中身を描画するクラスを定義します。ここでは進捗が80%未満であればTextViewに「まだだよ」と表示し、それ以上であれば「もうすぐだよ」と表示します。

class CustomTripProgressComponent(private val binding: CustomTripProgressBinding) : UIComponent() {
    override fun onAttached(mapboxNavigation: MapboxNavigation) {
        super.onAttached(mapboxNavigation)
        coroutineScope.launch {
            mapboxNavigation.flowRouteProgress().collect {
                val ratio = it.distanceTraveled / (it.distanceTraveled + it.distanceRemaining)
                binding.messageTextView.text = when {
                    ratio < 0.8 -> "まだまだだよ"
                    else -> "もうすぐだよ"
                }
            }
        }
    }
}

最後にcustomizeViewBindersを用いてCustomTripProgressViewBinderを登録します。

binding.navigationView.customizeViewBinders {
    infoPanelTripProgressBinder = CustomTripProgressViewBinder()
}

結果

https://www.youtube.com/shorts/UukXdFZ54iE

まとめ

NavigationViewを使用すると、コードを書くことなくナビゲーションアプリケーションが作成できます。また、スタイルやUIも柔軟にカスタマイズできます。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion