Em breve, vamos lançar uma nova estilização de mapas na Plataforma Google Maps. Essa atualização da estilização de mapas inclui uma nova paleta de cores padrão e melhorias nas experiências e usabilidade dos mapas. Todos os estilos de mapa vão ser atualizados automaticamente em março de 2025. Para mais informações sobre disponibilidade e como ativar o recurso antes, consulte Novo estilo de mapa para a Plataforma Google Maps.
Adicione um SupportNavigationFragment ou
NavigationView ao app. Esse elemento da interface
adiciona o mapa interativo e a interface de navegação guiada à sua atividade.
Use getSimulator() para simular o progresso do
veículo ao longo do trajeto para testar, depurar e demonstrar seu
app.
Compile e execute o app.
Achou o código?
Mostre/oculte o código Java para a atividade de navegação.
packagecom.example.navsdkmultidestination;importandroid.content.pm.PackageManager;importandroid.location.Location;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.CameraPerspective;importcom.google.android.libraries.navigation.ArrivalEvent;importcom.google.android.libraries.navigation.ListenableResultFuture;importcom.google.android.libraries.navigation.NavigationApi;importcom.google.android.libraries.navigation.Navigator;importcom.google.android.libraries.navigation.RoadSnappedLocationProvider;importcom.google.android.libraries.navigation.SimulationOptions;importcom.google.android.libraries.navigation.SupportNavigationFragment;importcom.google.android.libraries.navigation.TimeAndDistance;importcom.google.android.libraries.navigation.Waypoint;importjava.util.ArrayList;importjava.util.List;/***AnactivitythatdisplaysamapandanavigationUI,guidingtheuserfromtheircurrentlocation*tomultipledestinations,alsoknownaswaypoints.*/publicclassNavigationActivityMultiDestinationextendsAppCompatActivity{privatestaticfinalStringTAG=NavigationActivityMultiDestination.class.getSimpleName();privatestaticfinalStringDISPLAY_BOTH="both";privatestaticfinalStringDISPLAY_TOAST="toast";privatestaticfinalStringDISPLAY_LOG="log";privateNavigatormNavigator;privateRoadSnappedLocationProvidermRoadSnappedLocationProvider;privateSupportNavigationFragmentmNavFragment;privatefinalList<Waypoint>mWaypoints=newArrayList<>();privateNavigator.ArrivalListenermArrivalListener;privateNavigator.RouteChangedListenermRouteChangedListener;privateNavigator.RemainingTimeOrDistanceChangedListenermRemainingTimeOrDistanceChangedListener;privateRoadSnappedLocationProvider.LocationListenermLocationListener;privateBundlemSavedInstanceState;privatestaticfinalStringKEY_JOURNEY_IN_PROGRESS="journey_in_progress";privatebooleanmJourneyInProgress=false;//Setfieldsforrequestinglocationpermission.privatestaticfinalintPERMISSIONS_REQUEST_ACCESS_FINE_LOCATION=1;privatebooleanmLocationPermissionGranted;/***Setsupthenavigatorwhentheactivityiscreated.**@paramsavedInstanceStateTheactivitystatebundle.*/@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//Savethenavigatorstate,usedtodeterminewhetherajourneyisinprogress.mSavedInstanceState=savedInstanceState;if(mSavedInstanceState!=null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)){mJourneyInProgress=(mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS)!=0);}setContentView(R.layout.activity_main);//InitializetheNavigationSDK.initializeNavigationSdk();}/**Releasesnavigationlistenerswhentheactivityisdestroyed.*/@OverrideprotectedvoidonDestroy(){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);}}/**Savesthestateoftheappwhentheactivityispaused.*/@OverrideprotectedvoidonSaveInstanceState(BundleoutState){super.onSaveInstanceState(outState);if(mJourneyInProgress){outState.putInt(KEY_JOURNEY_IN_PROGRESS,1);}else{outState.putInt(KEY_JOURNEY_IN_PROGRESS,0);}}/***StartstheNavigationSDKandsetsthecameratofollowthedevice's location. Calls the*navigateToPlaces()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.",DISPLAY_BOTH);return;}//Getanavigator.NavigationApi.getNavigator(this,newNavigationApi.NavigatorListener(){/**SetsupthenavigationUIwhenthenavigatorisreadyforuse.*/@OverridepublicvoidonNavigatorReady(Navigatornavigator){displayMessage("Navigator ready.",DISPLAY_BOTH);mNavigator=navigator;mNavFragment=(SupportNavigationFragment)getSupportFragmentManager().findFragmentById(R.id.navigation_fragment);//Setthecameratofollowthedevicelocationwith'TILTED'drivingview.mNavFragment.getMapAsync(googleMap-> googleMap.followMyLocation(CameraPerspective.TILTED));//Navigatetothespecifiedplaces.navigateToPlaces();}/***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.",DISPLAY_BOTH);break;caseNavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:displayMessage("Error loading Navigation SDK: User did not accept "+"the Navigation Terms of Use.",DISPLAY_BOTH);break;caseNavigationApi.ErrorCode.NETWORK_ERROR:displayMessage("Error loading Navigation SDK: Network error.",DISPLAY_BOTH);break;caseNavigationApi.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);}}});}/**Requestsdirectionsfromtheuser's current location to a list of waypoints. */privatevoidnavigateToPlaces(){//Setupawaypointforeachplacethatwewanttogoto.createWaypoint("ChIJq6qq6jauEmsRJAf7FjrKnXI","Sydney Star");createWaypoint("ChIJ3S-JXmauEmsRUcIaWtf4MzE","Sydney Opera House");createWaypoint("ChIJLwgLFGmuEmsRzpDhHQuyyoU","Sydney Conservatorium of Music");//Ifthisjourneyisalreadyinprogress,noneedtorestartnavigation.//Thiscanhappenwhentheuserrotatesthedevice,orsendstheapptothebackground.if(mSavedInstanceState!=null
&& mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)
&& mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS)==1){return;}//Createafuturetoawaittheresultoftheasynchronousnavigatortask.ListenableResultFuture<Navigator.RouteStatus> pendingRoute=mNavigator.setDestinations(mWaypoints);//DefinetheactiontoperformwhentheSDKhasdeterminedtheroute.pendingRoute.setOnResultListener(newListenableResultFuture.OnResultListener<Navigator.RouteStatus>(){@OverridepublicvoidonResult(Navigator.RouteStatuscode){switch(code){caseOK:mJourneyInProgress=true;//HidethetoolbartomaximizethenavigationUI.if(getActionBar()!=null){getActionBar().hide();}//Registersomelistenersfornavigationevents.registerNavigationListeners();//Displaythetimeanddistancetoeachwaypoint.displayTimesAndDistances();//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.",DISPLAY_BOTH);break;caseNETWORK_ERROR:displayMessage("Error starting navigation: Network error.",DISPLAY_BOTH);break;caseROUTE_CANCELED:displayMessage("Error starting navigation: Route canceled.",DISPLAY_BOTH);break;default:displayMessage("Error starting navigation: "+String.valueOf(code),DISPLAY_BOTH);}}});}/***CreatesawaypointfromagivenplaceIDandtitle.**@paramplaceIdTheIDoftheplacetobeconvertedtoawaypoint.*@paramtitleAdescriptivetitleforthewaypoint.*/privatevoidcreateWaypoint(StringplaceId,Stringtitle){try{mWaypoints.add(Waypoint.builder().setPlaceIdString(placeId).setTitle(title).build());}catch(Waypoint.UnsupportedPlaceIdExceptione){displayMessage("Error starting navigation: Place ID is not supported: "+placeId,DISPLAY_BOTH);}}/**Displaysthecalculatedtraveltimeanddistancetoeachwaypoint.*/privatevoiddisplayTimesAndDistances(){List<TimeAndDistance>timesAndDistances=mNavigator.getTimeAndDistanceList();intleg=1;Stringmessage="You're on your way!";for(TimeAndDistancetimeAndDistance:timesAndDistances){message=message+"\nRoute leg: "+leg+++": Travel time (seconds): "+timeAndDistance.getSeconds()+". Distance (meters): "+timeAndDistance.getMeters();}displayMessage(message,DISPLAY_BOTH);}/***Registerssomeeventlistenerstoshowamessageandtakeothernecessarystepswhenspecific*navigationeventsoccur.*/privatevoidregisterNavigationListeners(){mArrivalListener=newNavigator.ArrivalListener(){@OverridepublicvoidonArrival(ArrivalEventarrivalEvent){displayMessage("onArrival: You've arrived at a waypoint: "+mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),DISPLAY_BOTH);//Startturn-by-turnguidanceforthenextlegoftheroute.if(arrivalEvent.isFinalDestination()){displayMessage("onArrival: You've arrived at the final destination.",DISPLAY_BOTH);}else{mNavigator.continueToNextDestination();mNavigator.startGuidance();}}};//Listensforarrivalatawaypoint.mNavigator.addArrivalListener(mArrivalListener);mRouteChangedListener=newNavigator.RouteChangedListener(){@OverridepublicvoidonRouteChanged(){displayMessage("onRouteChanged: The driver's route has changed. Current waypoint: "+mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),DISPLAY_LOG);}};//Listensforchangesintheroute.mNavigator.addRouteChangedListener(mRouteChangedListener);//Listensforroad-snappedlocationupdates.mRoadSnappedLocationProvider=NavigationApi.getRoadSnappedLocationProvider(getApplication());mLocationListener=newRoadSnappedLocationProvider.LocationListener(){@OverridepublicvoidonLocationChanged(Locationlocation){displayMessage("onLocationUpdated: Navigation engine has provided a new"+" road-snapped location: "+location.toString(),DISPLAY_LOG);}@OverridepublicvoidonRawLocationUpdate(Locationlocation){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=newNavigator.RemainingTimeOrDistanceChangedListener(){@OverridepublicvoidonRemainingTimeOrDistanceChanged(){displayMessage("onRemainingTimeOrDistanceChanged: Time or distance estimate"+" has changed.",DISPLAY_LOG);}};//Listensforchangesintimeordistance.mNavigator.addRemainingTimeOrDistanceChangedListener(60,100,mRemainingTimeOrDistanceChangedListener);}/**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,StringdisplayMedium){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);}}}
Adicionar um fragmento de navegação
O SupportNavigationFragment é o componente da interface que mostra
a saída visual da navegação, incluindo um mapa interativo e direções passo a passo. É possível declarar o fragmento no arquivo de layout XML, conforme mostrado abaixo:
Como alternativa a um fragmento, o componente da interface também está disponível como um
NavigationView. Observe as informações na parte superior
a descrição da classe, especialmente sobre a exigência de avançar o ciclo de vida
métodos.
Solicitar permissão de localização
O app precisa solicitar a permissão de localização para determinar
a localização do dispositivo.
Neste tutorial, mostramos o código necessário para pedir a permissão de localização exata.
Para mais detalhes, consulte o guia sobre Permissões do Android.
Adicionar a permissão como um filho do elemento <manifest> no Android
manifest:
Solicitar permissões de execução no app, oferecendo ao usuário a oportunidade
para permitir ou negar a permissão de localização. O código a seguir verifica se
usuário concedeu permissão de localização exata. Se não, ele solicita a permissão:
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.",DISPLAY_BOTH);return;}
Modifique o callback onRequestPermissionsResult() para processar o resultado da
solicitação de permissão:
Inicializar o SDK do Navigation e configurar uma jornada
A classe NavigationApi fornece uma lógica de inicialização
que autoriza o app a usar a navegação do Google. A classe Navigator
oferece controle sobre a configuração e a inicialização/parada de uma jornada de navegação.
Crie um método auxiliar para mostrar uma mensagem na tela e no registro.
Inicialize o SDK do Navigation e substitua o
Callback onNavigatorReady() para iniciar a navegação quando o navegador estiver
pronta:
NavigationApi.getNavigator(this,newNavigationApi.NavigatorListener(){/***SetsupthenavigationUIwhenthenavigatorisreadyforuse.*/@OverridepublicvoidonNavigatorReady(Navigatornavigator){displayMessage("Navigator ready.",DISPLAY_BOTH);mNavigator=navigator;mNavFragment=(SupportNavigationFragment)getFragmentManager().findFragmentById(R.id.navigation_fragment);//Setthecameratofollowthedevicelocationwith'TILTED'drivingview.mNavFragment.getCamera().followMyLocation(Camera.Perspective.TILTED);//Navigatetothespecifiedplaces.navigateToPlaces();}/***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.",DISPLAY_BOTH);break;caseNavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:displayMessage("Error loading Navigation SDK: User did not accept "+"the Navigation Terms of Use.",DISPLAY_BOTH);break;caseNavigationApi.ErrorCode.NETWORK_ERROR:displayMessage("Error loading Navigation SDK: Network error.",DISPLAY_BOTH);break;caseNavigationApi.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);}}});
Foi adicionado um método para criar um objeto Waypoint com base em um determinado ID e título de lugar.
Adicione um método para mostrar o tempo de percurso e a distância calculada para cada ponto de passagem.
privatevoiddisplayTimesAndDistances(){List<TimeAndDistance>timesAndDistances=mNavigator.getTimeAndDistanceList();intleg=1;Stringmessage="You'reonyourway!";for(TimeAndDistancetimeAndDistance:timesAndDistances){message=message+"\nRoute leg: "+leg+++": Travel time (seconds): "+timeAndDistance.getSeconds()+". Distance (meters): "+timeAndDistance.getMeters();}displayMessage(message,DISPLAY_BOTH);}
Defina todos os waypoints desta jornada. Pode ser que você receba uma mensagem de erro
se você usar IDs de lugares para os quais o navegador não pode traçar um trajeto. O app de exemplo
neste tutorial usa IDs de lugar para pontos de passagem na Austrália. Consulte as notas abaixo sobre como acessar diferentes IDs de lugar. Depois de calcular as direções, o SupportNavigationFragment mostra uma poligonal que representa o trajeto no mapa, com um marcador em cada waypoint.
privatevoidnavigateToPlaces(){//Setupawaypointforeachplacethatwewanttogoto.createWaypoint("ChIJq6qq6jauEmsRJAf7FjrKnXI","Sydney Star");createWaypoint("ChIJ3S-JXmauEmsRUcIaWtf4MzE","Sydney Opera House");createWaypoint("ChIJLwgLFGmuEmsRzpDhHQuyyoU","Sydney Conservatorium of Music");//Ifthisjourneyisalreadyinprogress,noneedtorestartnavigation.//Thiscanhappenwhentheuserrotatesthedevice,orsendstheapptothebackground.if(mSavedInstanceState!=null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS) && mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS)==1){return;}//Createafuturetoawaittheresultoftheasynchronousnavigatortask.ListenableResultFuture<Navigator.RouteStatus>pendingRoute=mNavigator.setDestinations(mWaypoints);//DefinetheactiontoperformwhentheSDKhasdeterminedtheroute.pendingRoute.setOnResultListener(newListenableResultFuture.OnResultListener<Navigator.RouteStatus>(){@OverridepublicvoidonResult(Navigator.RouteStatuscode){switch(code){caseOK:mJourneyInProgress=true;//HidethetoolbartomaximizethenavigationUI.if(getActionBar()!=null){getActionBar().hide();}//Registersomelistenersfornavigationevents.registerNavigationListeners();//Displaythetimeanddistancetoeachwaypoint.displayTimesAndDistances();//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.",DISPLAY_BOTH);break;caseNETWORK_ERROR:displayMessage("Error starting navigation: Network error.",DISPLAY_BOTH);break;caseROUTE_CANCELED:displayMessage("Error starting navigation: Route canceled.",DISPLAY_BOTH);break;default:displayMessage("Error starting navigation: "+String.valueOf(code),DISPLAY_BOTH);}}});}
Criar e executar o app
Conecte um dispositivo Android ao computador. Siga as instruções se quiser ativar as opções para desenvolvedores no seu dispositivo Android e configurar o sistema de modo a detectar o aparelho. Também é possível usar o AVD Manager para configurar um dispositivo virtual. Ao escolher um emulador, selecione uma imagem que inclua
as APIs do Google.
No Android Studio, clique na opção de menu Run ou no ícone do botão de reprodução.
Escolha um dispositivo quando solicitado.
Dicas para melhorar a experiência do usuário
O usuário precisa aceitar os Termos de Serviço do Google Navigation antes que a navegação fique disponível. Essa aceitação só é necessária uma vez. De
padrão, o SDK solicita a aceitação na primeira vez que o navegador é
invocado. Se preferir, você pode acionar a caixa de diálogo "Termos de serviço de navegação"
em um ponto inicial do fluxo de UX do app, como durante o registro ou login, usando
showTermsAndConditionsDialog().
A qualidade da navegação e a precisão do HEC melhoram significativamente se você usar
IDs de lugares para inicializar um waypoint, em vez de valores de latitude/longitude
destino.
Este exemplo deriva os waypoints de um lugar específico
do Google Ads. Confira outras maneiras de receber um ID de lugar:
[[["Fácil de entender","easyToUnderstand","thumb-up"],["Meu problema foi resolvido","solvedMyProblem","thumb-up"],["Outro","otherUp","thumb-up"]],[["Não contém as informações de que eu preciso","missingTheInformationINeed","thumb-down"],["Muito complicado / etapas demais","tooComplicatedTooManySteps","thumb-down"],["Desatualizado","outOfDate","thumb-down"],["Problema na tradução","translationIssue","thumb-down"],["Problema com as amostras / o código","samplesCodeIssue","thumb-down"],["Outro","otherDown","thumb-down"]],["Última atualização 2024-10-14 UTC."],[],[]]