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

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

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

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

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

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

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

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