複数の目的地のルートを確認する

このガイドでは、Navigation SDK for Android を使用して、アプリ内で複数の目的地(経由地)までのルートをプロットする方法について説明します。

概要

  1. プロジェクトを設定するで説明されているように、Navigation SDK をアプリに統合します。
  2. アプリに SupportNavigationFragment または NavigationView を追加します。この UI 要素により、インタラクティブな地図とターンバイターン ナビゲーションの UI がアクティビティに追加されます。
  3. NavigationApi クラスを使用して SDK を初期化します。
  4. ターンバイターン方式のナビゲーションを制御する Navigator を定義します。

    • setDestinations() を使用してデスティネーションを追加します。
    • startGuidance() でナビを開始します。
    • getSimulator() を使用して、アプリのテスト、デバッグ、デモのために、ルートに沿った車両の進行状況をシミュレートします。
  5. アプリをビルドして実行します。

コードの確認

ナビゲーション フラグメントを追加する

SupportNavigationFragment は、インタラクティブな地図やターンバイターンの経路案内など、ナビゲーションの視覚的な出力を表示する UI コンポーネントです。フラグメントは、次のように XML レイアウト ファイルで宣言できます。

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.google.android.libraries.navigation.SupportNavigationFragment"
    android:id="@+id/navigation_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

また、FragmentActivity.getSupportFragmentManager() を使用して、Android ドキュメントで説明されているように、フラグメントをプログラムで構築することもできます。

フラグメントの代わりに、UI コンポーネントを NavigationView として使用することもできます。ほとんどの場合、NavigationView と直接やり取りするのではなく、NavigationView のラッパーである SupportNavigationFragment を使用することをおすすめします。詳しくは、ナビゲーション マップの操作に関するベスト プラクティス をご覧ください。

位置情報の利用許可をリクエストする

デバイスの位置を特定するには、アプリで位置情報の利用許可をリクエストする必要があります。

このチュートリアルでは、高精度位置情報の利用許可をリクエストするために必要なコードを提供します。詳しくは、Android の権限に関するガイドをご覧ください。

  1. 権限を <manifest> 要素の子要素として Android マニフェストに追加します。

        <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.navsdkmultidestination">
            <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        </manifest>
    
  2. アプリで実行時の権限をリクエストして、ユーザーが位置情報の利用許可を許可または拒否できるようにします。次のコードでは、ユーザーが高精度位置パーミッションを許可したかどうかをチェックしています。許可していない場合は、パーミッションをリクエストします。

        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION },
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    
        if (!mLocationPermissionGranted) {
            displayMessage("Error loading Navigation SDK: "
                    + "The user has not granted location permission.", DISPLAY_BOTH);
            return;
        }
    
  3. パーミッション リクエストの結果を処理するよう onRequestPermissionsResult() コールバックをオーバーライドします。

        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                               @NonNull int[] grantResults) {
            mLocationPermissionGranted = false;
            switch (requestCode) {
                case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                    // If request is canceled, the result arrays are empty.
                    if (grantResults.length > 0
                            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        mLocationPermissionGranted = true;
                    }
                }
            }
        }
    

Navigation SDK を初期化して乗車ルートを構成する

NavigationApi クラスは、アプリが Google ナビゲーションを使用することを承認する初期化ロジックを提供します。Navigator クラスは、ナビゲーションの旅程の構成、開始、停止を制御します。

  1. 画面とログにメッセージを表示するヘルパー メソッドを作成します。

        private void displayMessage(String errorMessage, String displayMedium) {
            if (displayMedium.equals(DISPLAY_BOTH) || displayMedium.equals(DISPLAY_TOAST)) {
                Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
            }
    
            if (displayMedium.equals(DISPLAY_BOTH) || displayMedium.equals(DISPLAY_LOG)) {
                Log.d(TAG, errorMessage);
            }
        }
    
  2. Navigation SDK を初期化し、onNavigatorReady() コールバックをオーバーライドして、ナビゲーターの準備が整ったらナビゲーションを開始します。

        NavigationApi.getNavigator(this, new NavigationApi.NavigatorListener() {
                    /**
                     * Sets up the navigation UI when the navigator is ready for use.
                     */
                    @Override
                    public void onNavigatorReady(Navigator navigator) {
                        displayMessage("Navigator ready.", DISPLAY_BOTH);
                        mNavigator = navigator;
                        mNavFragment = (SupportNavigationFragment) getFragmentManager()
                                .findFragmentById(R.id.navigation_fragment);
    
                        // Set the camera to follow the device location with 'TILTED' driving view.
                        mNavFragment.getCamera().followMyLocation(Camera.Perspective.TILTED);
    
                        // Navigate to the specified places.
                        navigateToPlaces();
                    }
    
                    /**
                     * Handles errors from the Navigation SDK.
                     * @param errorCode The error code returned by the navigator.
                     */
                    @Override
                    public void onError(@NavigationApi.ErrorCode int errorCode) {
                        switch (errorCode) {
                            case NavigationApi.ErrorCode.NOT_AUTHORIZED:
                                displayMessage("Error loading Navigation SDK: Your API key is "
                                        + "invalid or not authorized to use the Navigation SDK.",
                                        DISPLAY_BOTH);
                                break;
                            case NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:
                                displayMessage("Error loading Navigation SDK: User did not accept "
                                        + "the Navigation Terms of Use.", DISPLAY_BOTH);
                                break;
                            case NavigationApi.ErrorCode.NETWORK_ERROR:
                                displayMessage("Error loading Navigation SDK: Network error.",
                                        DISPLAY_BOTH);
                                break;
                            case NavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING:
                                displayMessage("Error loading Navigation SDK: Location permission "
                                        + "is missing.", DISPLAY_BOTH);
                                break;
                            default:
                                displayMessage("Error loading Navigation SDK: " + errorCode,
                                        DISPLAY_BOTH);
                        }
                    }
                });
    
  3. 指定されたプレイス ID とタイトルから Waypoint オブジェクトを作成するメソッドを追加します。

        private void createWaypoint(String placeId, String title) {
            try {
                mWaypoints.add(
                  Waypoint.builder()
                         .setPlaceIdString(placeId)
                         .setTitle(title)
                         .build()
                );
            } catch (Waypoint.UnsupportedPlaceIdException e) {
                displayMessage("Error starting navigation: Place ID is not supported: " + placeId,
                        DISPLAY_BOTH);
            }
        }
    
  4. 計算された移動時間と距離を各経由地に表示するメソッドを追加しました。

        private void displayTimesAndDistances() {
            List<TimeAndDistance> timesAndDistances = mNavigator.getTimeAndDistanceList();
            int leg = 1;
            String message = "You're on your way!";
            for (TimeAndDistance timeAndDistance : timesAndDistances) {
                message = message + "\nRoute leg: " + leg++
                        + ": Travel time (seconds): " + timeAndDistance.getSeconds()
                        + ". Distance (meters): " + timeAndDistance.getMeters();
            }
            displayMessage(message, DISPLAY_BOTH);
        }
    
  5. このジャーニーのすべてのウェイポイントを設定します。(ナビゲーターがルートをプロットできない場所 ID を使用すると、エラーが発生する可能性があります。このチュートリアルのサンプルアプリでは、オーストラリアの経由地のプレイス ID を使用しています。別のプレイス ID を取得するについての以下の注をご覧ください)。ルートを計算すると、SupportNavigationFragment は、地図上にルートを表すポリラインを表示し、各経由地にマーカーを表示します。

        private void navigateToPlaces() {
    
            // Set up a waypoint for each place that we want to go to.
            createWaypoint("ChIJq6qq6jauEmsRJAf7FjrKnXI", "Sydney Star");
            createWaypoint("ChIJ3S-JXmauEmsRUcIaWtf4MzE", "Sydney Opera House");
            createWaypoint("ChIJLwgLFGmuEmsRzpDhHQuyyoU", "Sydney Conservatorium of Music");
    
            // If this journey is already in progress, no need to restart navigation.
            // This can happen when the user rotates the device, or sends the app to the background.
            if (mSavedInstanceState != null
                    && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)
                    && mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) == 1) {
                return;
            }
    
            // Create a future to await the result of the asynchronous navigator task.
            ListenableResultFuture<Navigator.RouteStatus> pendingRoute =
                    mNavigator.setDestinations(mWaypoints);
    
            // Define the action to perform when the SDK has determined the route.
            pendingRoute.setOnResultListener(
                    new ListenableResultFuture.OnResultListener<Navigator.RouteStatus>() {
                        @Override
                        public void onResult(Navigator.RouteStatus code) {
                            switch (code) {
                                case OK:
                                    mJourneyInProgress = true;
                                    // Hide the toolbar to maximize the navigation UI.
                                    if (getActionBar() != null) {
                                        getActionBar().hide();
                                    }
    
                                    // Register some listeners for navigation events.
                                    registerNavigationListeners();
    
                                    // Display the time and distance to each waypoint.
                                    displayTimesAndDistances();
    
                                    // Enable voice audio guidance (through the device speaker).
                                    mNavigator.setAudioGuidance(
                                            Navigator.AudioGuidance.VOICE_ALERTS_AND_GUIDANCE);
    
                                    // Simulate vehicle progress along the route for demo/debug builds.
                                    if (BuildConfig.DEBUG) {
                                        mNavigator.getSimulator().simulateLocationsAlongExistingRoute(
                                                new SimulationOptions().speedMultiplier(5));
                                    }
    
                                    // Start turn-by-turn guidance along the current route.
                                    mNavigator.startGuidance();
                                    break;
                                // Handle error conditions returned by the navigator.
                                case NO_ROUTE_FOUND:
                                    displayMessage("Error starting navigation: No route found.",
                                            DISPLAY_BOTH);
                                    break;
                                case NETWORK_ERROR:
                                    displayMessage("Error starting navigation: Network error.",
                                            DISPLAY_BOTH);
                                    break;
                                case ROUTE_CANCELED:
                                    displayMessage("Error starting navigation: Route canceled.",
                                            DISPLAY_BOTH);
                                    break;
                                default:
                                    displayMessage("Error starting navigation: "
                                            + String.valueOf(code), DISPLAY_BOTH);
                            }
                        }
                    });
        }
    

アプリをビルドして実行する

  1. Android デバイスをコンピュータに接続します。手順に沿って、Android デバイスでデベロッパー オプションを有効にし、デバイスを検出するようにシステムを設定します(または、Android Virtual Device(AVD)Manager を使用して仮想デバイスを設定することもできます。エミュレータを指定する際は、Google API を含むイメージを選択する必要があります。)
  2. Android Studio で [Run] メニュー オプション(またはプレイボタン アイコン)をクリックします。表示される指示に沿ってデバイスを選択します。

ユーザー エクスペリエンスを向上させるためのヒント

  • ナビゲーションを利用するには、Google ナビゲーションの利用規約に同意する必要があります。この同意は 1 回のみ必要です。デフォルトでは、ナビゲータが初めて呼び出されたときに、SDK は受け入れを求めるプロンプトを表示します。必要に応じて、showTermsAndConditionsDialog() を使用して、アプリの UX フローの早い段階(登録やログインなど)でナビゲーションの利用規約ダイアログをトリガーできます。
  • 緯度/経度の目的地ではなく、プレイス ID を使用して経由地を初期化すると、ナビゲーションの品質と ETA の精度が大幅に向上します。
  • このサンプルでは、特定の場所 ID から経由地を取得します。プレイス ID を取得するその他の方法は次のとおりです。

  • プレイス ID 検索ツールを使用して、特定の場所のプレイス ID を取得します。

  • Geocoding API を使用して、特定の住所のプレイス ID を検索します。Geocoding API は、経由地の完全で明確な住所がある場合に有効です。ジオコーディングのベスト プラクティス ガイドをご覧ください。

  • Places API テキスト検索を使用して、特定の住所のプレイス ID を見つけます。Places API は、経由地の住所が不完全またはあいまいな場合に有効です。ジオコーディングのベスト プラクティス ガイドをご覧ください。