다중 대상 경로 탐색

이 가이드에 따라 Android용 Navigation SDK를 사용하여 앱 내에서 여러 대상 지역(경유지라고도 함)으로 가는 경로를 표시합니다.

개요

  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"/>

또는 Android 문서에 설명된 대로 FragmentActivity.getSupportFragmentManager()를 사용하여 프로그래매틱 방식으로 프래그먼트를 구성할 수 있습니다.

프래그먼트의 대안으로 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를 사용합니다. 다양한 장소 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 스튜디오에서 Run 메뉴 옵션 (또는 재생 버튼 아이콘)을 클릭합니다. 표시되는 메시지에 따라 기기를 선택합니다.

사용자 환경 개선을 위한 힌트

  • 내비게이션을 사용하려면 사용자가 Google 내비게이션 서비스 약관에 동의해야 합니다. 이 동의는 한 번만 필요합니다. 기본적으로 SDK는 내비게이터가 처음 호출될 때 동의를 요청합니다. 원하는 경우 `showTermsAndConditionsDialog()`를 사용하여 가입 또는 로그인과 같이 앱의 UX 흐름에서 초기에 내비게이션 서비스 약관 대화상자를 트리거할 수 있습니다. showTermsAndConditionsDialog()
  • 위도/경도 대상 지역 대신 장소 ID를 사용하여 경유지를 초기화하면 내비게이션 품질과 도착 예정 시간 정확도가 크게 개선됩니다.
  • 이 샘플은 특정 장소 ID에서 경유지를 파생합니다. 장소 ID를 가져오는 다른 방법은 다음과 같습니다.

  • 장소 ID 찾기를 사용하여 특정 위치의 장소 ID를 가져옵니다.

  • Geocoding API를 사용하여 지정된 주소의 장소 ID 를 찾습니다. Geocoding API는 경유지의 완전하고 명확한 주소가 있는 경우에 잘 작동합니다. 지오코딩 권장사항 가이드를 참고하세요.

  • Places API 텍스트 검색을 사용하여 지정된 주소의 장소 ID를 찾습니다. Places API는 경유지의 불완전하거나 명확하지 않은 주소가 있는 경우에 잘 작동합니다. 지오코딩 권장사항 가이드를 참고하세요.