Navigation SDK는 현재 일부 고객만 사용할 수 있습니다. 자세한 내용은 영업팀에 문의하세요.
새로운 지도 스타일 지정 기능이 곧 Google Maps Platform에 도입될 예정입니다. 이번 지도 스타일 지정 업데이트에는 새로운 기본 색상 팔레트가 추가되었으며 지도 환경 및 사용성이 개선되었습니다. 모든 지도 스타일은 2025년 3월에 자동으로 업데이트됩니다. 사용 가능 여부 및 이전에 선택한 방법에 대한 자세한 내용은 Google Maps Platform의 새 지도 스타일을 참고하세요.
Android용 Navigation SDK를 사용하면 지도에 표시되는 기본 제공 UI 컨트롤과 요소를 지정하여 지도의 사용자 환경을 수정할 수 있습니다. 내비게이션 UI의 시각적 모양도 조정할 수 있습니다. 허용되는 탐색 UI 수정사항에 관한 가이드라인은 정책 페이지를 참고하세요.
이 문서에서는 다음 두 가지 방법으로 지도의 사용자 인터페이스를 수정하는 방법을 설명합니다.
지도 UI 컨트롤은 올바른 위치를 보장하기 위해 탐색 뷰에 맞춤 UI 요소를 배치하는 데 권장되는 방법입니다. 내장 레이아웃이 변경되면 Android용 Navigation SDK가 맞춤 컨트롤의 위치를 자동으로 조정합니다.
각 위치에 한 번에 하나의 맞춤 컨트롤 뷰를 설정할 수 있습니다. 디자인에 여러 UI 요소가 필요한 경우 ViewGroup에 배치하고 setCustomControl 메서드에 전달할 수 있습니다.
이 맞춤 컨트롤 위치를 사용하려면 위치 CustomControlPosition.SECONDARY_HEADER를 setCustomControl에 전달합니다.
기본적으로 탐색 모드의 화면 레이아웃은 기본 헤더 아래에 있는 보조 헤더의 위치를 제공합니다. 이 보조 헤더는 필요에 따라 표시됩니다(예: 차선 안내). 앱은 맞춤 콘텐츠에 레이아웃의 이 보조 헤더 위치를 사용할 수 있습니다. 이 기능을 사용하면 기본 보조 헤더 콘텐츠가 모두 제어됩니다. 탐색 뷰에 배경이 있는 경우 이 배경은 보조 헤더로 가려진 채 그대로 유지됩니다.
앱에서 맞춤 컨트롤을 삭제하면 기본 보조 헤더가 그 자리에 표시될 수 있습니다.
맞춤 보조 헤더 위치는 상단 가장자리를 기본 헤더의 하단 가장자리에 맞춥니다. 이 위치는 portrait mode에서만 지원됩니다. landscape mode에서는 보조 헤더를 사용할 수 없으며 레이아웃이 변경되지 않습니다.
하단 시작
세로 모드 방향의 하단 시작 맞춤 컨트롤 위치
가로 모드 방향의 하단 시작 맞춤 컨트롤 위치
이 맞춤 컨트롤 위치를 사용하려면 위치 CustomControlPosition.BOTTOM_START_BELOW를 setCustomControl에 전달합니다.
이 맞춤 컨트롤 위치는 지도의 하단 시작 모서리에 있습니다. portrait mode 및 landscape mode 모두에서 예상 도착 시간 카드 또는 맞춤 바닥글 위에 표시되며 (둘 다 없는 경우 지도 하단에 표시됨) 가운데 맞춤 버튼과 Google 로고를 비롯한 Nav SDK 요소가 맞춤 컨트롤 뷰의 높이를 고려하여 위로 이동합니다. 이 컨트롤은 표시되는 지도 경계 내에 배치되므로 지도의 하단 또는 시작 가장자리에 추가된 패딩도 이 컨트롤의 위치를 변경합니다.
하단 끝
세로 모드 방향의 하단 맞춤 컨트롤 위치
가로 모드 방향의 하단 맞춤 컨트롤 위치
이 맞춤 컨트롤 위치를 사용하려면 위치 CustomControlPosition.BOTTOM_END_BELOW를 setCustomControl에 전달합니다.
이 맞춤 컨트롤 위치는 지도의 맨 하단에 있습니다. portrait mode에서는 도착 예정 시간 카드 또는 맞춤 바닥글 위에 표시되며 (둘 다 없는 경우 지도 하단에 표시됨) landscape mode에서는 지도 하단에 정렬됩니다. 끝쪽(LTR의 경우 오른쪽)에 표시되는 모든 Nav SDK 요소가 맞춤 컨트롤 뷰의 높이를 고려하여 위로 이동합니다. 이 컨트롤은 표시되는 지도 경계 내에 배치되므로 지도의 하단 또는 끝 가장자리에 추가된 패딩도 이 컨트롤의 위치를 변경합니다.
바닥글
세로 모드 방향의 바닥글 맞춤 컨트롤 위치
가로 모드 방향의 바닥글 맞춤 컨트롤 위치
이 맞춤 컨트롤 위치를 사용하려면 위치 CustomControlPosition.FOOTER를 setCustomControl에 전달합니다.
이 맞춤 컨트롤 위치는 맞춤 바닥글 뷰용으로 설계되었습니다. Nav SDK ETA 카드가 표시되면 이 컨트롤이 그 위에 표시됩니다. 그렇지 않으면 컨트롤이 지도 하단에 정렬됩니다. BOTTOM_START_BELOW 및 BOTTOM_END_BELOW 맞춤 컨트롤과 달리 이 컨트롤은 표시되는 지도 경계 외부에 배치됩니다. 즉, 지도에 패딩을 추가해도 이 컨트롤의 위치는 변경되지 않습니다.
portrait mode에서는 맞춤 바닥글이 전체 너비입니다. CustomControlPosition.BOTTOM_START_BELOW 및 CustomControlPosition.BOTTOM_END_BELOW 위치의 맞춤 컨트롤과 가운데로 다시 가져오기 버튼 및 Google 로고와 같은 Nav SDK UI 요소는 맞춤 컨트롤 바닥글 위에 배치됩니다. 셰이본의 기본 위치는 맞춤 바닥글 높이를 고려합니다.
landscape mode에서 맞춤 바닥글은 Nav SDK ETA 카드와 마찬가지로 절반 너비이며 시작 측면 (LTR의 경우 왼쪽)에 정렬됩니다. CustomControlPosition.BOTTOM_START_BELOW 위치의 맞춤 컨트롤과 가운데 재정렬 버튼 및 Google 로고와 같은 Nav SDK UI 요소는 맞춤 컨트롤 바닥글 위에 배치됩니다. CustomControlPosition.BOTTOM_END_BELOW 위치의 맞춤 컨트롤과 끝쪽 (LTR의 경우 오른쪽)의 모든 Nav SDK UI 요소는 지도 하단에 정렬된 상태로 유지됩니다. 바닥글이 지도의 끝쪽까지 확장되지 않으므로 맞춤 바닥글이 있는 경우에도 화살표의 기본 위치는 변경되지 않습니다.
CustomControlPosition.BOTTOM_START_BELOW 및 CustomControlPosition.BOTTOM_END_BELOW 위치의 맞춤 컨트롤과 가운데로 다시 가져오기 버튼 및 Google 로고와 같은 Nav SDK UI 요소가 맞춤 컨트롤 바닥글 위에 배치됩니다.
지도 UI 액세서리
Android용 Navigation SDK는 Android용 Google 지도 애플리케이션에 있는 것과 유사하게 탐색 중에 표시되는 UI 액세서리를 제공합니다.
이 섹션에 설명된 대로 이러한 컨트롤의 공개 상태 또는 시각적 모양을 조정할 수 있습니다. 여기에서 변경하면 다음 탐색 세션 중에 반영됩니다.
packagecom.example.navsdkcustomization;importandroid.content.pm.PackageManager;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.os.Bundle;importandroid.util.Log;importandroid.widget.Toast;importandroidx.annotation.NonNull;importandroidx.appcompat.app.AppCompatActivity;importandroidx.core.app.ActivityCompat;importandroidx.core.content.ContextCompat;importcom.google.android.gms.maps.GoogleMap;importcom.google.android.gms.maps.GoogleMap.CameraPerspective;importcom.google.android.gms.maps.OnMapReadyCallback;importcom.google.android.gms.maps.model.BitmapDescriptorFactory;importcom.google.android.gms.maps.model.LatLng;importcom.google.android.gms.maps.model.Marker;importcom.google.android.gms.maps.model.MarkerOptions;importcom.google.android.libraries.navigation.ListenableResultFuture;importcom.google.android.libraries.navigation.NavigationApi;importcom.google.android.libraries.navigation.Navigator;importcom.google.android.libraries.navigation.SimulationOptions;importcom.google.android.libraries.navigation.StylingOptions;importcom.google.android.libraries.navigation.SupportNavigationFragment;importcom.google.android.libraries.navigation.Waypoint;/**AnactivitythatdisplaysamapandacustomizednavigationUI.*/publicclassNavigationActivityCustomizationextendsAppCompatActivity{privatestaticfinalStringTAG=NavigationActivityCustomization.class.getSimpleName();privateNavigatormNavigator;privateSupportNavigationFragmentmNavFragment;privateGoogleMapmMap;//DefinetheSydneyOperaHousebyspecifyingitsplaceID.privatestaticfinalStringSYDNEY_OPERA_HOUSE="ChIJ3S-JXmauEmsRUcIaWtf4MzE";//Setfieldsforrequestinglocationpermission.privatestaticfinalintPERMISSIONS_REQUEST_ACCESS_FINE_LOCATION=1;privatebooleanmLocationPermissionGranted;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//InitializetheNavigationSDK.initializeNavigationSdk();}/***StartstheNavigationSDKandsetsthecameratofollowthedevice's location. Calls the*navigateToPlace()methodwhenthenavigatorisready.*/privatevoidinitializeNavigationSdk(){/**Requestlocationpermission,sothatwecangetthelocationofthe*device.Theresultofthepermissionrequestishandledbyacallback,*onRequestPermissionsResult.*/if(ContextCompat.checkSelfPermission(this.getApplicationContext(),android.Manifest.permission.ACCESS_FINE_LOCATION)==PackageManager.PERMISSION_GRANTED){mLocationPermissionGranted=true;}else{ActivityCompat.requestPermissions(this,newString[]{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.");return;}//Getanavigator.NavigationApi.getNavigator(this,newNavigationApi.NavigatorListener(){/**SetsupthenavigationUIwhenthenavigatorisreadyforuse.*/@OverridepublicvoidonNavigatorReady(Navigatornavigator){displayMessage("Navigator ready.");mNavigator=navigator;mNavFragment=(SupportNavigationFragment)getSupportFragmentManager().findFragmentById(R.id.navigation_fragment);//Getthemap.mNavFragment.getMapAsync(newOnMapReadyCallback(){@OverridepublicvoidonMapReady(GoogleMapmap){mMap=map;//Navigatetoaplace,specifiedbyPlaceID.navigateToPlace(SYDNEY_OPERA_HOUSE);}});}/***HandleserrorsfromtheNavigationSDK.**@paramerrorCodeTheerrorcodereturnedbythenavigator.*/@OverridepublicvoidonError(@NavigationApi.ErrorCodeinterrorCode){switch(errorCode){caseNavigationApi.ErrorCode.NOT_AUTHORIZED:displayMessage("Error loading Navigation SDK: Your API key is "+"invalid or not authorized to use the Navigation SDK.");break;caseNavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:displayMessage("Error loading Navigation SDK: User did not accept "+"the Navigation Terms of Use.");break;caseNavigationApi.ErrorCode.NETWORK_ERROR:displayMessage("Error loading Navigation SDK: Network error.");break;caseNavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING:displayMessage("Error loading Navigation SDK: Location permission "+"is missing.");break;default:displayMessage("Error loading Navigation SDK: "+errorCode);}}});}/**CustomizesthenavigationUIandthemap.*/privatevoidcustomizeNavigationUI(){//Setcustomcolorsforthenavigator.mNavFragment.setStylingOptions(newStylingOptions().primaryDayModeThemeColor(0xff1A237E).secondaryDayModeThemeColor(0xff3F51B5).primaryNightModeThemeColor(0xff212121).secondaryNightModeThemeColor(0xff424242).headerLargeManeuverIconColor(0xffffff00).headerSmallManeuverIconColor(0xffffa500).headerNextStepTypefacePath("/system/fonts/NotoSerif-BoldItalic.ttf").headerNextStepTextColor(0xff00ff00).headerNextStepTextSize(20f).headerDistanceTypefacePath("/system/fonts/NotoSerif-Italic.ttf").headerDistanceValueTextColor(0xff00ff00).headerDistanceUnitsTextColor(0xff0000ff).headerDistanceValueTextSize(20f).headerDistanceUnitsTextSize(18f).headerInstructionsTypefacePath("/system/fonts/NotoSerif-BoldItalic.ttf").headerInstructionsTextColor(0xffffff00).headerInstructionsFirstRowTextSize(24f).headerInstructionsSecondRowTextSize(20f).headerGuidanceRecommendedLaneColor(0xffffa500));mMap.setTrafficEnabled(false);//Placeamarkeratthefinaldestination.if(mNavigator.getCurrentRouteSegment()!=null){LatLngdestinationLatLng=mNavigator.getCurrentRouteSegment().getDestinationLatLng();BitmapdestinationMarkerIcon=BitmapFactory.decodeResource(getResources(),R.drawable.ic_person_pin_48dp);mMap.addMarker(newMarkerOptions().position(destinationLatLng).icon(BitmapDescriptorFactory.fromBitmap(destinationMarkerIcon)).title("Destination marker"));//Listenforataponthemarker.mMap.setOnMarkerClickListener(newGoogleMap.OnMarkerClickListener(){@OverridepublicbooleanonMarkerClick(Markermarker){displayMessage("Marker tapped: "+marker.getTitle()+", at location "+marker.getPosition().latitude+", "+marker.getPosition().longitude);//Theeventhasbeenhandled.returntrue;}});}//Setthecameratofollowthedevicelocationwith'TILTED'drivingview.mMap.followMyLocation(CameraPerspective.TILTED);}/***Requestsdirectionsfromtheuser's current location to a specific place (provided by the*GooglePlacesAPI).*/privatevoidnavigateToPlace(StringplaceId){Waypointdestination;try{destination=Waypoint.builder().setPlaceIdString(placeId).build();}catch(Waypoint.UnsupportedPlaceIdExceptione){displayMessage("Error starting navigation: Place ID is not supported.");return;}//Createafuturetoawaittheresultoftheasynchronousnavigatortask.ListenableResultFuture<Navigator.RouteStatus> pendingRoute=mNavigator.setDestination(destination);//DefinetheactiontoperformwhentheSDKhasdeterminedtheroute.pendingRoute.setOnResultListener(newListenableResultFuture.OnResultListener<Navigator.RouteStatus>(){@OverridepublicvoidonResult(Navigator.RouteStatuscode){switch(code){caseOK://HidethetoolbartomaximizethenavigationUI.if(getActionBar()!=null){getActionBar().hide();}//CustomizethenavigationUI.customizeNavigationUI();//Enablevoiceaudioguidance(throughthedevicespeaker).mNavigator.setAudioGuidance(Navigator.AudioGuidance.VOICE_ALERTS_AND_GUIDANCE);//Simulatevehicleprogressalongtheroutefordemo/debugbuilds.if(BuildConfig.DEBUG){mNavigator.getSimulator().simulateLocationsAlongExistingRoute(newSimulationOptions().speedMultiplier(5));}//Startturn-by-turnguidancealongthecurrentroute.mNavigator.startGuidance();break;//Handleerrorconditionsreturnedbythenavigator.caseNO_ROUTE_FOUND:displayMessage("Error starting navigation: No route found.");break;caseNETWORK_ERROR:displayMessage("Error starting navigation: Network error.");break;caseROUTE_CANCELED:displayMessage("Error starting navigation: Route canceled.");break;default:displayMessage("Error starting navigation: "+String.valueOf(code));}}});}/**Handlestheresultoftherequestforlocationpermissions.*/@OverridepublicvoidonRequestPermissionsResult(intrequestCode,@NonNullString[]permissions,@NonNullint[]grantResults){mLocationPermissionGranted=false;switch(requestCode){casePERMISSIONS_REQUEST_ACCESS_FINE_LOCATION:{//Ifrequestiscanceled,theresultarraysareempty.if(grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){mLocationPermissionGranted=true;}}}}/***Showsamessageonscreenandinthelog.Usedwhensomethinggoeswrong.**@paramerrorMessageThemessagetodisplay.*/privatevoiddisplayMessage(StringerrorMessage){Toast.makeText(this,errorMessage,Toast.LENGTH_LONG).show();Log.d(TAG,errorMessage);}}
privateSupportNavigationFragmentmNavFragment;mNavFragment=(SupportNavigationFragment)getFragmentManager().findFragmentById(R.id.navigation_fragment);// Set the styling options on the fragment.mNavFragment.setStylingOptions(newStylingOptions().primaryDayModeThemeColor(0xff1A237E).secondaryDayModeThemeColor(0xff3F51B5).primaryNightModeThemeColor(0xff212121).secondaryNightModeThemeColor(0xff424242).headerLargeManeuverIconColor(0xffffff00).headerSmallManeuverIconColor(0xffffa500).headerNextStepTypefacePath("/system/fonts/NotoSerif-BoldItalic.ttf").headerNextStepTextColor(0xff00ff00).headerNextStepTextSize(20f).headerDistanceTypefacePath("/system/fonts/NotoSerif-Italic.ttf").headerDistanceValueTextColor(0xff00ff00).headerDistanceUnitsTextColor(0xff0000ff).headerDistanceValueTextSize(20f).headerDistanceUnitsTextSize(18f).headerInstructionsTypefacePath("/system/fonts/NotoSerif-BoldItalic.ttf").headerInstructionsTextColor(0xffffff00).headerInstructionsFirstRowTextSize(24f).headerInstructionsSecondRowTextSize(20f).headerGuidanceRecommendedLaneColor(0xffffa500));
교통정보 레이어 사용 중지
GoogleMap.setTrafficEnabled()를 사용하여 지도에서 교통 레이어를 사용 설정하거나 중지합니다. 이 설정은 지도 전체에 표시되는 교통량 밀도 표시에 영향을 미칩니다. 그러나 내비게이터에서 표시한 경로의 교통 표시에는 영향을 미치지 않습니다.
privateGoogleMapmMap;// Get the map, and when the async call returns, setTrafficEnabled// (callback will be on the UI thread)mMap=mNavFragment.getMapAsync(navMap->navMap.setTrafficEnabled(false));
신호등 및 정지 표지판 사용 설정
내비게이션이 진행되는 동안 지도에 신호등과 정지 표지판을 표시하도록 설정하면 경로와 이동 조작에 관한 추가 컨텍스트를 제공할 수 있습니다.
기본적으로 내비게이션 SDK에서는 신호등과 정지 표지판이 사용 중지되어 있습니다. 이 기능을 사용 설정하려면 각 지형지물에 대해 DisplayOptions를 개별적으로 호출합니다.
AUTO Navigation SDK가 기기 위치 및 현지 시간에 따라 적절한 모드를 결정할 수 있도록 합니다.
FORCE_NIGHT: 야간 모드를 강제로 사용 설정합니다.
FORCE_DAY: 주간 모드를 강제로 사용 설정합니다.
다음 예는 탐색 프래그먼트 내에서 야간 모드를 강제로 사용 설정하는 방법을 보여줍니다.
// Force night mode on.mNavFragment.setForceNightMode(FORCE_NIGHT);
경로 목록 표시
먼저 뷰를 만들고 계층 구조에 추가합니다.
voidsetupDirectionsListView(){// Create the view.DirectionsListViewdirectionsListView=newDirectionsListView(getApplicationContext());// Add the view to your view hierarchy.ViewGroupgroup=findViewById(R.id.directions_view);group.addView(directionsListView);// Add a button to your layout to close the directions list view.ImageButtonbutton=findViewById(R.id.close_directions_button);// this button is part of the container we hide in the next line.button.setOnClickListener(v->findViewById(R.id.directions_view_container).setVisibility(View.GONE));}
NavigationView와 마찬가지로 수명 주기 이벤트를 DirectionsListView에 전달해야 합니다. 예를 들면 다음과 같습니다.
사용자 인터페이스가 너무 많은 정보로 인해 복잡해지면 기본값 (2개)보다 적은 수의 대체 경로를 표시하거나 대체 경로를 전혀 표시하지 않으면 복잡성을 줄일 수 있습니다. 경로를 가져오기 전에 다음 열거형 값 중 하나를 사용하여 RoutingOptions.alternateRoutesStrategy() 메서드를 호출하여 이 옵션을 구성할 수 있습니다.
// Enable the trip progress bar.mNavFragment.setTripProgressBarEnabled(true);
경로 진행률 표시줄 위치 지정
막대의 왼쪽이 속도계, Google 로고, 가운데로 다시 버튼 (표시되는 경우)의 왼쪽과 대략적으로 정렬됩니다. 너비는 12dp입니다.
경로 진행률 표시줄은 고정된 높이를 유지합니다. 소형 기기의 세로 공간 제한에 맞추기 위해 경로 진행률 표시줄의 표시 상태와 높이는 화면 높이의 지정된 브레이크포인트를 기반으로 조정됩니다. 이러한 브레이크포인트는 기기 방향 및 지도에서 화면을 실제로 차지하는 공간과는 무관합니다.
화면 높이
이동 진행률 표시줄 표시 여부
경로 진행률 표시줄 높이
경로 진행률 표시줄의 y축 위치
소형: 0dp~551dp
표시 안 됨
해당 사항 없음
해당 사항 없음
중형: 552~739dp
표시
130 dp
시작 측면 컨트롤 위 (속도계 / Google 로고 / 중앙 재설정 버튼)
대형: 740dp 이상
표시
290 dp
시작 측면 컨트롤 위 (속도계 / Google 로고 / 중앙 재설정 버튼)
경로 진행률 표시줄이 회전 카드 또는 기타 내비게이션 UI 요소와 겹치면 다른 요소 아래에 표시됩니다.
Prompt Visibility API (실험용)
Prompt Visibility API를 사용하면 탐색 SDK UI 요소가 표시되기 직전과 요소가 삭제되는 즉시 콜백을 수신하는 리스너를 추가하여 탐색 SDK에서 생성된 UI 요소와 자체 맞춤 UI 요소 간의 충돌을 방지할 수 있습니다. 코드 샘플을 포함한 자세한 내용은 실시간 중단 구성 페이지의 메시지 표시 API 섹션을 참고하세요.
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-05-08(UTC)"],[[["The Android Navigation SDK offers UI customization through predefined map UI controls and accessories, allowing developers to tailor the navigation experience."],["Developers can style elements like the navigation header, traffic layer, and speed limit display, as well as manage night mode and route display options."],["Custom markers and floating text can be added using Google Maps APIs, with adherence to attribution guidelines."],["`SupportNavigationFragment` is recommended for better integration and handling of the NavigationView."],["Always call UI customization methods on the UI thread to avoid rendering issues."]]],[]]