規劃多目的地路線

請按照本指南的說明,使用 Navigation SDK for Android 在應用程式中繪製前往多個目的地 (也稱為路徑點) 的路線。

總覽

  1. 按照「設定專案」一節所述,將 Navigation SDK 整合至應用程式。
  2. SupportNavigationFragmentNavigationView 新增至應用程式。這個 UI 元素會將互動式地圖和即時路線導航 UI 新增至活動。
  3. 使用 NavigationApi 類別初始化 SDK。
  4. 定義 Navigator 以控制即時路線導航:

  5. 建構並執行應用程式。

查看程式碼

package com.example.navsdkmultidestination;

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.maps.GoogleMap.CameraPerspective;
import com.google.android.libraries.navigation.ArrivalEvent;
import com.google.android.libraries.navigation.ListenableResultFuture;
import com.google.android.libraries.navigation.NavigationApi;
import com.google.android.libraries.navigation.Navigator;
import com.google.android.libraries.navigation.RoadSnappedLocationProvider;
import com.google.android.libraries.navigation.SimulationOptions;
import com.google.android.libraries.navigation.SupportNavigationFragment;
import com.google.android.libraries.navigation.TimeAndDistance;
import com.google.android.libraries.navigation.Waypoint;
import java.util.ArrayList;
import java.util.List;

/**
 * An activity that displays a map and a navigation UI, guiding the user from their current location
 * to multiple destinations, also known as waypoints.
 */
public class NavigationActivityMultiDestination extends AppCompatActivity {

  private static final String TAG = NavigationActivityMultiDestination.class.getSimpleName();
  private static final String DISPLAY_BOTH = "both";
  private static final String DISPLAY_TOAST = "toast";
  private static final String DISPLAY_LOG = "log";

  private Navigator mNavigator;
  private RoadSnappedLocationProvider mRoadSnappedLocationProvider;
  private SupportNavigationFragment mNavFragment;
  private final List<Waypoint> mWaypoints = new ArrayList<>();

  private Navigator.ArrivalListener mArrivalListener;
  private Navigator.RouteChangedListener mRouteChangedListener;
  private Navigator.RemainingTimeOrDistanceChangedListener mRemainingTimeOrDistanceChangedListener;
  private RoadSnappedLocationProvider.LocationListener mLocationListener;

  private Bundle mSavedInstanceState;
  private static final String KEY_JOURNEY_IN_PROGRESS = "journey_in_progress";
  private boolean mJourneyInProgress = false;

  // Set fields for requesting location permission.
  private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
  private boolean mLocationPermissionGranted;

  /**
   * Sets up the navigator when the activity is created.
   *
   * @param savedInstanceState The activity state bundle.
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Save the navigator state, used to determine whether a journey is in progress.
    mSavedInstanceState = savedInstanceState;
    if (mSavedInstanceState != null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)) {
      mJourneyInProgress = (mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) != 0);
    }

    setContentView(R.layout.activity_main);

    // Initialize the Navigation SDK.
    initializeNavigationSdk();
  }

  /** Releases navigation listeners when the activity is destroyed. */
  @Override
  protected void onDestroy() {
    super.onDestroy();

    if ((mJourneyInProgress) && (this.isFinishing())) {
      mNavigator.removeArrivalListener(mArrivalListener);
      mNavigator.removeRouteChangedListener(mRouteChangedListener);
      mNavigator.removeRemainingTimeOrDistanceChangedListener(
          mRemainingTimeOrDistanceChangedListener);
      if (mRoadSnappedLocationProvider != null) {
        mRoadSnappedLocationProvider.removeLocationListener(mLocationListener);
      }
      displayMessage("OnDestroy: Released navigation listeners.", DISPLAY_LOG);
    }
  }

  /** Saves the state of the app when the activity is paused. */
  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (mJourneyInProgress) {
      outState.putInt(KEY_JOURNEY_IN_PROGRESS, 1);
    } else {
      outState.putInt(KEY_JOURNEY_IN_PROGRESS, 0);
    }
  }

  /**
   * Starts the Navigation SDK and sets the camera to follow the device's location. Calls the
   * navigateToPlaces() method when the navigator is ready.
   */
  private void initializeNavigationSdk() {
    /*
     * Request location permission, so that we can get the location of the
     * device. The result of the permission request is handled by a callback,
     * onRequestPermissionsResult.
     */
    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;
    }

    // Get a navigator.
    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)
                    getSupportFragmentManager().findFragmentById(R.id.navigation_fragment);

            // Set the camera to follow the device location with 'TILTED' driving view.
            mNavFragment.getMapAsync(
                googleMap -> googleMap.followMyLocation(CameraPerspective.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);
            }
          }
        });
  }

  /** Requests directions from the user's current location to a list of waypoints. */
  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);
            }
          }
        });
  }

  /**
   * Creates a waypoint from a given place ID and title.
   *
   * @param placeId The ID of the place to be converted to a waypoint.
   * @param title A descriptive title for the 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);
    }
  }

  /** Displays the calculated travel time and distance to each waypoint. */
  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);
  }

  /**
   * Registers some event listeners to show a message and take other necessary steps when specific
   * navigation events occur.
   */
  private void registerNavigationListeners() {
    mArrivalListener =
        new Navigator.ArrivalListener() {
          @Override
          public void onArrival(ArrivalEvent arrivalEvent) {
            displayMessage(
                "onArrival: You've arrived at a waypoint: "
                    + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                DISPLAY_BOTH);
            // Start turn-by-turn guidance for the next leg of the route.
            if (arrivalEvent.isFinalDestination()) {
              displayMessage("onArrival: You've arrived at the final destination.", DISPLAY_BOTH);
            } else {
              mNavigator.continueToNextDestination();
              mNavigator.startGuidance();
            }
          }
        };
    // Listens for arrival at a waypoint.
    mNavigator.addArrivalListener(mArrivalListener);

    mRouteChangedListener =
        new Navigator.RouteChangedListener() {
          @Override
          public void onRouteChanged() {
            displayMessage(
                "onRouteChanged: The driver's route has changed. Current waypoint: "
                    + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
                DISPLAY_LOG);
          }
        };
    // Listens for changes in the route.
    mNavigator.addRouteChangedListener(mRouteChangedListener);

    // Listens for road-snapped location updates.
    mRoadSnappedLocationProvider = NavigationApi.getRoadSnappedLocationProvider(getApplication());
    mLocationListener =
        new RoadSnappedLocationProvider.LocationListener() {
          @Override
          public void onLocationChanged(Location location) {
            displayMessage(
                "onLocationUpdated: Navigation engine has provided a new"
                    + " road-snapped location: "
                    + location.toString(),
                DISPLAY_LOG);
          }

          @Override
          public void onRawLocationUpdate(Location location) {
            displayMessage(
                "onLocationUpdated: Navigation engine has provided a new"
                    + " raw location: "
                    + location.toString(),
                DISPLAY_LOG);
          }
        };
    if (mRoadSnappedLocationProvider != null) {
      mRoadSnappedLocationProvider.addLocationListener(mLocationListener);
    } else {
      displayMessage("ERROR: Failed to get a location provider", DISPLAY_LOG);
    }

    mRemainingTimeOrDistanceChangedListener =
        new Navigator.RemainingTimeOrDistanceChangedListener() {
          @Override
          public void onRemainingTimeOrDistanceChanged() {
            displayMessage(
                "onRemainingTimeOrDistanceChanged: Time or distance estimate" + " has changed.",
                DISPLAY_LOG);
          }
        };
    // Listens for changes in time or distance.
    mNavigator.addRemainingTimeOrDistanceChangedListener(
        60, 100, mRemainingTimeOrDistanceChangedListener);
  }

  /** Handles the result of the request for location permissions. */
  @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;
          }
        }
    }
  }

  /**
   * Shows a message on screen and in the log. Used when something goes wrong.
   *
   * @param errorMessage The message to display.
   */
  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);
    }
  }
}

新增導覽片段

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 說明文件的說明,以程式輔助方式建構 Fragment。

除了片段之外,UI 元件也可以做為 NavigationView 使用。在大多數情況下,我們建議使用 SupportNavigationFragment,這是 NavigationView 的包裝函式,而非直接與 NavigationView 互動。詳情請參閱導覽地圖互動最佳做法

要求位置存取權

應用程式必須要求位置存取權,才能判斷裝置的位置。

本教學課程會提供您需要的程式碼,用於要求精確位置權限。詳情請參閱 Android 權限指南。

  1. 在 Android 資訊清單中,將權限新增為 <manifest> 元素的子項:

    <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。請參閱下方的附註,瞭解如何取得不同的 Place 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 虛擬裝置管理工具 (AVD Manager) 來設定虛擬裝置。選擇模擬器時,請務必選取包含 Google API 的映像檔)。
  2. 在 Android Studio 中,按一下「Run」(執行) 選單選項 (或播放按鈕圖示),然後按照系統提示選擇裝置。

改善使用者體驗的提示

  • 使用者必須接受《Google 導航服務條款》,才能使用導航功能。您只需接受一次。根據預設,SDK 會在第一次叫用導覽器時提示接受。如有需要,您可以使用 showTermsAndConditionsDialog(),在應用程式的 UX 流程中 (例如註冊或登入期間) 的早期觸發導覽服務條款對話方塊。
  • 如果您使用地點 ID 初始化路線點,而不是經緯度目的地,導航品質和預估到達時間的準確度會大幅提升。
  • 這個範例會從特定地點 ID 衍生出中途點。取得地點 ID 的其他方式包括: