Навигация по маршруту с несколькими пунктами назначения

Следуйте этому руководству, чтобы проложить маршрут в своем приложении к нескольким пунктам назначения, также называемым путевыми точками, с помощью Navigation SDK для Android.

Обзор

  1. Интегрируйте Navigation SDK в свое приложение, как описано в разделе Настройка проекта .
  2. Добавьте SupportNavigationFragment или NavigationView в свое приложение. Этот элемент пользовательского интерфейса добавляет интерактивную карту и пошаговую навигацию в вашу активность.
  3. Используйте класс NavigationApi для инициализации SDK.
  4. Определите Navigator для управления пошаговой навигацией:

    • Добавьте пункты назначения с помощью setDestinations() .
    • Начните навигацию с помощью startGuidance() .
    • Используйте getSimulator() для моделирования движения транспортного средства по маршруту, для тестирования, отладки и демонстрации вашего приложения.
  5. Создайте и запустите свое приложение.

Посмотреть код

Добавить фрагмент навигации

SupportNavigationFragment — это компонент пользовательского интерфейса, который отображает визуальный вывод навигации, включая интерактивную карту и пошаговые инструкции. Вы можете объявить фрагмент в вашем 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"/>

В качестве альтернативы вы можете создать фрагмент программно, как описано в документации Android , используя FragmentActivity.getSupportFragmentManager() .

В качестве альтернативы фрагменту компонент пользовательского интерфейса также доступен как NavigationView . В большинстве случаев мы рекомендуем использовать SupportNavigationFragment , который является оболочкой для NavigationView , вместо прямого взаимодействия с NavigationView . Для получения дополнительной информации см. раздел Лучшие практики взаимодействия с картой навигации .

Запросить разрешение на местоположение

Ваше приложение должно запросить разрешение на определение местоположения, чтобы определить местоположение устройства.

В этом руководстве представлен код, необходимый для запроса разрешения на точное местоположение. Для получения более подробной информации см. руководство по разрешениям 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;
                    }
                }
            }
        }
    

Инициализируйте навигационный 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. Добавьте метод для создания объекта 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. Установите все путевые точки для этого путешествия. (Обратите внимание, что вы можете получить сообщение об ошибке, если используете идентификаторы мест, для которых навигатор не может проложить маршрут. Пример приложения в этом руководстве использует идентификаторы мест для путевых точек в Австралии. См. примечания ниже о получении различных идентификаторов мест .) После расчета маршрута 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 для настройки виртуального устройства. При выборе эмулятора убедитесь, что вы выбрали образ, включающий API Google.)
  2. В Android Studio нажмите на пункт меню «Выполнить» (или на значок кнопки воспроизведения). Выберите устройство, как будет предложено.

Советы по улучшению пользовательского опыта

  • Пользователь должен принять Условия обслуживания Google Navigation, прежде чем навигация станет доступной. Это принятие требуется только один раз. По умолчанию SDK запрашивает принятие при первом вызове навигатора. При желании вы можете вызвать диалоговое окно Условий обслуживания навигации на ранней стадии потока UX вашего приложения, например, во время регистрации или входа, используя showTermsAndConditionsDialog() .
  • Качество навигации и точность расчетного времени прибытия значительно улучшаются, если для инициализации путевой точки использовать идентификаторы мест, а не широту/долготу пункта назначения.
  • Этот пример выводит путевые точки из определенных идентификаторов мест. Другие способы получить идентификатор места включают следующее:

  • Используйте поиск идентификаторов мест , чтобы получить идентификаторы мест для определенных местоположений.

  • Используйте API геокодирования , чтобы найти идентификатор места для заданного адреса. API геокодирования работает хорошо, если у вас есть полные, недвусмысленные адреса для путевых точек. См. руководство по лучшим практикам геокодирования .

  • Используйте текстовый поиск API Places , чтобы найти идентификатор места для заданного адреса. API Places хорошо работает, если у вас неполные или неоднозначные адреса для путевых точек. См. руководство по лучшим практикам геокодирования .