Navigation turn-by-turn UI
The Mapbox Navigation UI SDK gives you all of the tools that you need to add turn-by-turn navigation to your apps.
Get up and running in a few minutes with our drop-in turn-by-turn navigation, or build a more custom turn-by-turn navigation app with our UI components.
Install the Navigation UI SDK
You'll need to add the Navigation UI SDK as a dependency before developing your app with the Navigation UI components. This dependency is different from the one used to compile the core Mapbox Navigation SDK, but it will still include everything from the core Mapbox Navigation SDK. If you're using the Navigation UI SDK, you don't have to declare the Mapbox Navigation SDK as well. If you only declare the Navigation UI SDK in your project's Gradle file, the Mapbox Navigation SDK will automatically be included.
Note that while we show how to insert the stable version of the Navigation UI SDK inside of your project, you can also use the nightly build/SNAPSHOT or the beta version if one is available. You can find the dependency given below in the MavenCentral repository.
repositories {
mavenCentral()
maven { url 'https://mapbox.bintray.com/mapbox' }
}
dependencies {
implementation 'com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.28.0'
}
Launch the Navigation UI
With either a DirectionsRoute
from NavigationRoute
or two Point
objects (origin and destination), you can launch the UI with NavigationLauncher
from within your Activity
:
// Route fetched from NavigationRouteDirectionsRoute route = ... boolean simulateRoute = true; // Create a NavigationLauncherOptions object to package everything togetherNavigationLauncherOptions options = NavigationLauncherOptions.builder().directionsRoute(route).shouldSimulateRoute(simulateRoute).build(); // Call this method with Context from within an ActivityNavigationLauncher.startNavigation(this, options);
NavigationViewOptions
NavigationViewOptions
provides a way to pass variables to a NavigationView
. This class can be used with NavigationLauncher
or when starting navigation with a custom implementation of NavigationView
.
You must provide either a valid DirectionsRoute
object, or both an origin and destination Point
objects. If you provide both, only the DirectionsRoute
will be used.
NavigationViewOptions options = NavigationViewOptions.builder().directionsRoute(route).shouldSimulateRoute(simulateRoute).build();
Unit Type (Metric / Imperial)
Metric / imperial unit formatting is based on the voice unit type you used for fetching the DirectionsRoute
with NavigationRoute
. The NavigationView
will look at the option from the API request and use this for the UI formatting. The NavigationRoute
Builder
takes an Android Context
so that if you don't specify a voice unit type, the default will be based on the device Locale
(from Configuration
).
// this being ContextNavigationRoute.builder(this) .accessToken(Mapbox.getAccessToken()) .origin(origin) .destination(destination) .voiceUnits(DirectionsCriteria.METRIC) .build()
NavigationView Activity Example
You can also inflate the entire navigation UI in your Activity
or Fragment
rather than using NavigationLauncher
.
To use this implementation, there is some setup you have to do to ensure the View
works properly:
Step 1
The NavigationView
has lifecycle methods to ensure the View
properly handles Android configuration changes or user interactions. You must also call navigationView.initialize(OnNavigationReadyCallback callback);
when NavigationView
is inflated and NavigationView#onCreate()
has been called.
Calling initialize()
will ultimately call onNavigationReady()
once all components for the View
have been properly initialized.
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {setTheme(R.style.Theme_AppCompat_NoActionBar);super.onCreate(savedInstanceState);setContentView(R.layout.activity_navigation);navigationView = findViewById(R.id.navigationView);navigationView.onCreate(savedInstanceState);navigationView.initialize(this);} @Overridepublic void onStart() {super.onStart();navigationView.onStart();} @Overridepublic void onResume() {super.onResume();navigationView.onResume();} @Overridepublic void onLowMemory() {super.onLowMemory();navigationView.onLowMemory();} @Overridepublic void onBackPressed() {// If the navigation view didn't need to do anything, call superif (!navigationView.onBackPressed()) {super.onBackPressed();}} @Overrideprotected void onSaveInstanceState(Bundle outState) {navigationView.onSaveInstanceState(outState);super.onSaveInstanceState(outState);} @Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);navigationView.onRestoreInstanceState(savedInstanceState);} @Overridepublic void onPause() {super.onPause();navigationView.onPause();} @Overridepublic void onStop() {super.onStop();navigationView.onStop();} @Overrideprotected void onDestroy() {super.onDestroy();navigationView.onDestroy();}
Step 2
Your Activity
or Fragment
must implement OnNavigationReadyCallback
. This interface includes the callback for when the turn-by-turn UI is ready to start - onNavigationReady(boolean isRunning)
is your cue to start navigation with NavigationView#startNavigation(NavigationViewOptions options)
.
The boolean isRunning
will always be true upon the first initialization of the NavigationView
. If will be true if the NavigationView
was previously initialized and navigation has already started - for example, upon second initialization of an Activity
or Fragment
like from a configuration change.
NavigationViewOptions
holds all of the custom data, settings, and listeners that you can provide to the NavigationView
.
@Overridepublic void onNavigationReady(boolean isRunning) {NavigationViewOptions options = NavigationViewOptions.builder().directionsRoute(directionsRoute).shouldSimulateRoute(simulateRoute).build(); navigationView.startNavigation(options);}
NavigationView Fragment Example
@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.navigation_view_fragment_layout, container);} @Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);navigationView = view.findViewById(R.id.navigation_view_fragment);navigationView.onCreate(savedInstanceState);navigationView.initialize(this);} @Overridepublic void onStart() {super.onStart();navigationView.onStart();} @Overridepublic void onResume() {super.onResume();navigationView.onResume();} @Overridepublic void onSaveInstanceState(@NonNull Bundle outState) {navigationView.onSaveInstanceState(outState);super.onSaveInstanceState(outState);} @Overridepublic void onViewStateRestored(@Nullable Bundle savedInstanceState) {super.onViewStateRestored(savedInstanceState);if (savedInstanceState != null) {navigationView.onRestoreInstanceState(savedInstanceState);}} @Overridepublic void onPause() {super.onPause();navigationView.onPause();} @Overridepublic void onStop() {super.onStop();navigationView.onStop();} @Overridepublic void onLowMemory() {super.onLowMemory();navigationView.onLowMemory();} @Overridepublic void onDestroyView() {super.onDestroyView();navigationView.onDestroy();} @Overridepublic void onNavigationReady(boolean isRunning) {Point origin = Point.fromLngLat(ORIGIN_LONGITUDE, ORIGIN_LATITUDE);Point destination = Point.fromLngLat(DESTINATION_LONGITUDE, DESTINATION_LATITUDE);NavigationViewOptions options = NavigationViewOptions.builder().directionsRoute(directionsRoute).shouldSimulateRoute(true).navigationListener(this).build();navigationView.startNavigation(options);}
Initialize NavigationView with a CameraPosition
You can initialize the NavigationView
with a CameraPosition
of your choice. This way, the MapView
does not need to zoom in to the initial Location
of the device from the world view.
CameraPosition initialPosition = new CameraPosition.Builder().target(new LatLng(ORIGIN.latitude(), ORIGIN.longitude())).zoom(INITIAL_ZOOM).build(); navigationView.initialize(this, initialPosition);
Listening to the NavigationView
Using NavigationView
in your XML also gives you the ability to listen to different updates or events that may occur during navigation. Both the ProgressChangeListener
and MilestoneEventListener
from our core SDK are able to be added, as well as three others: NavigationListener
, RouteListener
, and FeedbackListener
.
NavigationListener
onCancelNavigation()
: Will be triggered when the user clicks on the cancel "X" icon while navigating.onNavigationFinished()
: Will be triggered whenMapboxNavigation
has finished and the service is completely shutdown.onNavigationRunning()
: Will be triggered whenMapboxNavigation
has been initialized and the user is navigating the given route.
RouteListener
allowRerouteFrom(Point offRoutePoint)
: Will trigger in an off-route scenario.- Given the
Point
the user has gone off-route, this listener can return true or false. - Returning true will allow the SDK to proceed with the re-route process and fetch a new route with this given off-route
Point
. - Returning false will stop the re-route process and the user will continue without a new route in the direction they are traveling.
- Given the
onOffRoute(Point offRoutePoint)
: Will trigger only ifRouteListener#allowRerouteFrom(Point)
returns true.- This serves as the official off-route event and will continue the process to fetch a new route with the given off-route
Point
.
- This serves as the official off-route event and will continue the process to fetch a new route with the given off-route
onRerouteAlong(DirectionsRoute directionsRoute)
: Will trigger when a newDirectionsRoute
has been retrieved post off-route.- This is the new route the user will be following until another off route event is triggered.
onFailedReroute(String errorMessage)
: Will trigger if the request for a newDirectionsRoute
fails.- Provides the error message from the directions API used to retrieve the
DirectionsRoute
.
- Provides the error message from the directions API used to retrieve the
FeedbackListener
onFeedbackOpened()
: Will be triggered when the feedback bottomsheet is opened by a user while navigating.onFeedbackCancelled()
: Will be triggered when the feedback bottomsheet is opened by a user while navigating but then dismissed without clicking on a specificFeedbackItem
in the list.onFeedbackSent(FeedbackItem feedbackItem)
: Will be triggered when the feedback bottomsheet is opened by a user while navigating and then the user clicks on a specificFeedbackItem
in the list.
BannerInstructionsListener
willDisplay(BannerInstructions instructions)
: Will be triggered when aBannerInstructions
is about to be displayed. The listener gives you the option to override any values and pass as the return value, which will be the value used for the banner instructions. You can returnnull
and the instructions will be ignored.
SpeechAnnouncementListener
willVoice(SpeechAnnouncement announcement)
: Will be triggered when a voice announcement is about to be voiced. The listener gives you the option to override any values and pass as the return value, which will be the value used for the voice announcement. You can returnnull
and the announcement will be ignored.
To add these listeners, you can add them to your NavigationViewOptions
before you call navigationView.startNavigation(NavigationViewOptions options)
:
NavigationViewOptions options = NavigationViewOptions.builder() .navigationListener(this) .routeListener(this) .feedbackListener(this) .build();
Please note these listeners are only available if you are adding NavigationView
to your Activity
or Fragment
layout XML via NavigationViewOptions
. You are not able to add them to NavigationLauncherOptions
.
Styling the NavigationView
You can also style the NavigationView
colors. This includes the style of the map and/or route. To do this, provide a light and dark style in the XML where you have put your NavigationView
:
<com.mapbox.services.android.navigation.ui.v5.NavigationView
android:id="@+id/navigationView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationLightTheme="@style/NavigationViewLight"
app:navigationDarkTheme="@style/NavigationViewDark"
...
/>
Please note: each style must provide a value for every custom attribute or have a parent style NavigationViewLight
/ NavigationViewDark
- otherwise the View
will not properly inflate. Our default Mapbox style will be used if you do not provide a style for either of the light or dark theme attributes.
An example of how to create your own style can be found by looking at one of our default styles like R.style.NavigationViewLight
:
<style name="MyCustomTheme" parent="NavigationViewLight">
<item name="navigationViewPrimary">@color/mapbox_navigation_route_alternative_congestion_red</item>
<item name="navigationViewSecondary">@color/mapbox_navigation_route_layer_blue</item>
<!-- Map style URL -->
<item name="navigationViewMapStyle">@string/navigation_guidance_day_v3</item>
</style>
Here are a two more examples of custom themes. CustomNavigationMapRoute
is for the route line shown and is used in CustomNavigationViewLight
which allows you to customize the remaining NavigationView
colors, as well as the map style. Both have comments outlining where the given color should show on the screen:
<resources>
<style name="CustomNavigationMapRoute" parent="@style/NavigationMapRoute">
<!-- Main color for the route line -->
<item name="routeColor">#4882C6</item>
<!-- Outline color for the route line -->
<item name="routeShieldColor">#2C5F99</item>
<!-- Color for moderate traffic along the route line -->
<item name="routeModerateCongestionColor">#FFAB65</item>
<!-- Color for severe traffic along the route line -->
<item name="routeSevereCongestionColor">#E85552</item>
<!-- Scales -->
<item name="routeScale">1.0</item>
</style>
<style name="CustomNavigationViewLight" parent="@style/NavigationViewLight">
<!-- The main turn banner view at the top of the screen -->
<!-- Background color of the banner -->
<item name="navigationViewBannerBackground">#FFFFFF</item>
<!-- Color for the primary label that displays the turn name -->
<item name="navigationViewBannerPrimaryText">#37516F</item>
<!-- Color for the secondary label that occasionally appears underneath the primary label -->
<item name="navigationViewBannerSecondaryText">#E637516F</item>
<!-- Primary color for the turn arrow icons-->
<item name="navigationViewBannerManeuverPrimary">#37516F</item>
<!-- Secondary color for the turn arrow icons (e.g. the line segment that forks off) -->
<item name="navigationViewBannerManeuverSecondary">#4D37516F</item>
<!-- Alternate background color for the dropdown list of upcoming steps -->
<item name="navigationViewListBackground">#FAFAFA</item>
<!-- The summary view along the bottom of the screen -->
<!-- Background color of the summary view -->
<item name="navigationViewPrimary">#FFFFFF</item>
<!-- Tint color for icons in the summary view -->
<item name="navigationViewSecondary">#28353D</item>
<!-- Accent color for elements such as the recenter button -->
<item name="navigationViewAccent">#4882C6</item>
<!-- Color for the main duration label in the summary view -->
<item name="navigationViewPrimaryText">#424242</item>
<!-- Color for the secondary distance and ETA label in the summary view -->
<item name="navigationViewSecondaryText">#424242</item>
<!-- Custom colors for progress bars displayed during navigation -->
<item name="navigationViewProgress">#4B75A4</item>
<item name="navigationViewProgressBackground">#39587B</item>
<!-- Custom colors for the route line and traffic -->
<item name="navigationViewRouteStyle">@style/CustomNavigationMapRoute</item>
<!-- Map style -->
<item name="navigationViewMapStyle">mapbox://styles/mapbox/navigation-guidance-day-v2</item>
</style>
</resources>
Please reference the diagram below to see where these attribute names align within the actual UI:
Letter | Resource name |
---|---|
A | navigationViewPrimary |
B | navigationViewSecondary |
C | navigationViewSecondaryText |
D | navigationViewPrimaryText |
E | navigationViewSecondary |
F | navigationViewSecondary |
G | navigationViewAccent |
H | navigationViewLocationLayerStyle> mapbox_gpsDrawable |
I | navigationViewRouteStyle > upcomingManeuverArrowBorderColor |
J | navigationViewRouteStyle > upcomingManeuverArrowColor |
K | navigationViewRouteStyle > routeColor |
L | navigationViewRouteStyle > routeSevereCongestionColor |
M | navigationViewRouteStyle > routeShieldColor |
N | navigationViewMapStyle |
O | navigationViewRouteOverviewDrawable |
P | navigationViewBannerSecondaryText |
Q | navigationViewBannerPrimaryText |
R | navigationViewBannerBackground |
S | navigationViewBannerManeuverPrimary |
Day and Night mode
If you're using NavigationLauncher
or NavigationView
within your own Activity
or Fragment
the view will update based on whatever the current mode is in the Activity
. The current night mode is determined by AppCompatDelegate#getDefaultNightMode()
.
InstructionView
You also have the option to add the custom View
s used in the turn-by-turn UI to your XML. The top View
that displays the maneuver image, instruction text, and sound button is called InstructionView
.
<com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView
android:id="@+id/instructionView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Once inflated in your Activity
, the InstructionView
can be updated with RouteProgress
and Milestone
objects inside a ProgressChangeListener
and MilestoneEventListener
respectively.
@Overridepublic void onProgressChange(Location location, RouteProgress routeProgress) { instructionView.updateDistanceWith(routeProgress);} @Overridepublic void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {instructionView.updateBannerInstructionsWith(milestone);}
Prior to the first time you want to update the InstructionView
, you can control the distance formatting with InstructionView#setDistanceFormatter(DistanceFormatter distanceFormatter)
.
This will determine how distances are displayed in the view:
String unitType = DirectionsCriteria.METRIC; String language = Locale.US.getLanguage(); int roundingIncrement = NavigationConstants.ROUNDING_INCREMENT_TWENTY_FIVE; DistanceFormatter distanceFormatter = new DistanceFormatter(getContext(), language, unitType, roundingIncrement); instructionView.setDistanceFormatter(distanceFormatter);}
Note: It is fine if this is not set, the view will create its own based on inferred parameters from the device's Android configuration. Please also make sure to set our default theme: R.style.NavigationViewLight
(or create your own) and set it in your Activity
or Fragment
before super.onCreate()
. The custom View
s will now look for the attributes in the default theme to set text and background colors:
@Overrideprotected void onCreate(Bundle savedInstanceState) {setTheme(R.style.NavigationViewLight);super.onCreate(savedInstanceState);setContentView(R.layout.activity_navigation);...}
NavigationMapRoute
You can use NavigationMapRoute
to draw the route line on your map. Instantiate it with aMapView
and MapboxMap
, then add a DirectionsRoute
from our Directions API. The DirectionsRoute
will automatically be added (even in off-route scenarios) if you instantiate with MapboxNavigation
. You can also style the route with a given style:
NavigationMapRoute mapRoute = new NavigationMapRoute(navigation, mapView, mapboxMap, styleRes);}
The given style will determine route color, congestion colors, and the route scale:
<style name="NavigationMapRoute">
<!-- Colors -->
<item name="routeColor">@color/mapbox_navigation_route_layer_blue</item>
<item name="routeModerateCongestionColor">@color/mapbox_navigation_route_layer_congestion_yellow</item>
<item name="routeSevereCongestionColor">@color/mapbox_navigation_route_layer_congestion_red</item>
<item name="routeShieldColor">@color/mapbox_navigation_route_shield_layer_color</item>
<!-- Scales -->
<item name="routeScale">1.0</item>
</style>q
NavigationCamera
Driven by DynamicCamera
engine, the NavigationCamera
holds all of the logic needed to drive a MapboxMap
camera that reacts and adjusts to the current progress along a DirectionsRoute
.
To create an instance of NavigationCamera
, you need a MapboxMap
, MapboxNavigation
, and LocationComponent
object:
NavigationCamera camera = new NavigationCamera(mapboxMap, mapboxNavigation, locationComponent);}
Calling NavigationCamera#start(DirectionsRoute route)
will begin an animation to the start of theDirectionsRoute
you provided:
camera.start(directionsRoute);}
The NavigationCamera
has two tracking modes: NAVIGATION_TRACKING_MODE_GPS
and NAVIGATION_TRACKING_MODE_NORTH
. They offer two different behaviors. MODE_GPS
follows the Location
updates from the device based on the values provided by DynamicCamera
. MODE_NORTH
does the same, but with a bearing that is always zero, so the camera will always be pointed north. To adjust these, use:
camera.updateCameraTrackingMode(NavigationCamera.NAVIGATION_TRACKING_MODE_NORTH);}
NavigationCamera#showRouteOverview(int[] padding)
will also adjust the camera to the bounds of the DirectionsRoute
being traveled along with the given padding which is passed.
NavigationCamera#resetCameraPositonWith(NAVIGATION_TRACKING_MODE_GPS)
will reset the camera to the last known position update and will resume tracking of future updates with the mode you pass - in this case, tracking will resume with GPS tracking.
Loading banner instructions into a TextView
If you would like to use your own TextView
and load a given BannerText
instruction, you can do so with the InstructionLoader
. The loader takes care of organizing text, loading road shields, and abbreviating text (if it doesn't fit in the TextView
). Paired with our MilestoneEventListener
, you can load instruction updates into your TextView
:
@Overridepublic void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {if (milestone instanceof BannerInstructionMilestone) {BannerText primaryInstruction = ((BannerInstructionMilestone) milestone).getBannerInstructions().primary();InstructionLoader loader = new InstructionLoader(textView, primaryInstruction);loader.loadInstruction();}}}