此自定义控件位置专为自定义页脚视图而设计。如果 Nav SDK ETA 卡片可见,此控件会位于其上方。如果未设置,则控件会与地图底部对齐。与 BOTTOM_START_BELOW 和 BOTTOM_END_BELOW 自定义控件不同,此控件位于可见地图边界之外,这意味着向地图添加的任何内边距都不会更改此控件的位置。
在 portrait mode 中,自定义页脚是全宽的。CustomControlPosition.BOTTOM_START_BELOW 和 CustomControlPosition.BOTTOM_END_BELOW 位置的自定义控件,以及重新居中按钮和 Google 徽标等 Nav SDK 界面元素,都位于自定义控件页脚上方。箭头的默认位置会考虑自定义页脚高度。
在 landscape mode 中,自定义页脚占一半宽度,并与起始边(LTR 中的左侧)对齐,就像 Nav SDK ETA 卡片一样。CustomControlPosition.BOTTOM_START_BELOW 位置的自定义控件和 Nav SDK 界面元素(例如重新居中按钮和 Google 徽标)位于自定义控件页脚上方。CustomControlPosition.BOTTOM_END_BELOW 位置的自定义控件和底部(对于 LTR,位于右侧)的任何 Nav SDK 界面元素都与地图底部对齐。当存在自定义页脚时,箭头的默认位置不会发生变化,因为页脚不会延伸到地图的末端。
CustomControlPosition.BOTTOM_START_BELOW 和 CustomControlPosition.BOTTOM_END_BELOW 位置的自定义控件,以及重新居中按钮和 Google 徽标等 Nav SDK 界面元素位于自定义控件页脚上方。
地图界面配件
Navigation SDK for Android 提供在导航期间显示的界面配件,这些配件与 Android 版 Google 地图应用中显示的配件类似。您可以按照本部分所述的方式调整这些控件的可见性或视觉外观。您在此处所做的更改会在下一次导航会话中反映出来。
package com.example.navsdkcustomization;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.CameraPerspective;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.libraries.navigation.ListenableResultFuture;
import com.google.android.libraries.navigation.NavigationApi;
import com.google.android.libraries.navigation.Navigator;
import com.google.android.libraries.navigation.SimulationOptions;
import com.google.android.libraries.navigation.StylingOptions;
import com.google.android.libraries.navigation.SupportNavigationFragment;
import com.google.android.libraries.navigation.Waypoint;
/** An activity that displays a map and a customized navigation UI. */
public class NavigationActivityCustomization extends AppCompatActivity {
private static final String TAG = NavigationActivityCustomization.class.getSimpleName();
private Navigator mNavigator;
private SupportNavigationFragment mNavFragment;
private GoogleMap mMap;
// Define the Sydney Opera House by specifying its place ID.
private static final String SYDNEY_OPERA_HOUSE = "ChIJ3S-JXmauEmsRUcIaWtf4MzE";
// Set fields for requesting location permission.
private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
private boolean mLocationPermissionGranted;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the Navigation SDK.
initializeNavigationSdk();
}
/**
* Starts the Navigation SDK and sets the camera to follow the device's location. Calls the
* navigateToPlace() method when the navigator is ready.
*/
private void initializeNavigationSdk() {
/*
* Request location permission, so that we can get the location of the
* device. The result of the permission request is handled by a callback,
* onRequestPermissionsResult.
*/
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.");
return;
}
// Get a navigator.
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.");
mNavigator = navigator;
mNavFragment =
(SupportNavigationFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_fragment);
// Get the map.
mNavFragment.getMapAsync(
new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap map) {
mMap = map;
// Navigate to a place, specified by Place ID.
navigateToPlace(SYDNEY_OPERA_HOUSE);
}
});
}
/**
* 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.");
break;
case NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED:
displayMessage(
"Error loading Navigation SDK: User did not accept "
+ "the Navigation Terms of Use.");
break;
case NavigationApi.ErrorCode.NETWORK_ERROR:
displayMessage("Error loading Navigation SDK: Network error.");
break;
case NavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING:
displayMessage(
"Error loading Navigation SDK: Location permission " + "is missing.");
break;
default:
displayMessage("Error loading Navigation SDK: " + errorCode);
}
}
});
}
/** Customizes the navigation UI and the map. */
private void customizeNavigationUI() {
// Set custom colors for the navigator.
mNavFragment.setStylingOptions(
new StylingOptions()
.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);
// Place a marker at the final destination.
if (mNavigator.getCurrentRouteSegment() != null) {
LatLng destinationLatLng = mNavigator.getCurrentRouteSegment().getDestinationLatLng();
Bitmap destinationMarkerIcon =
BitmapFactory.decodeResource(getResources(), R.drawable.ic_person_pin_48dp);
mMap.addMarker(
new MarkerOptions()
.position(destinationLatLng)
.icon(BitmapDescriptorFactory.fromBitmap(destinationMarkerIcon))
.title("Destination marker"));
// Listen for a tap on the marker.
mMap.setOnMarkerClickListener(
new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
displayMessage(
"Marker tapped: "
+ marker.getTitle()
+ ", at location "
+ marker.getPosition().latitude
+ ", "
+ marker.getPosition().longitude);
// The event has been handled.
return true;
}
});
}
// Set the camera to follow the device location with 'TILTED' driving view.
mMap.followMyLocation(CameraPerspective.TILTED);
}
/**
* Requests directions from the user's current location to a specific place (provided by the
* Google Places API).
*/
private void navigateToPlace(String placeId) {
Waypoint destination;
try {
destination =
Waypoint.builder().setPlaceIdString(placeId).build();
} catch (Waypoint.UnsupportedPlaceIdException e) {
displayMessage("Error starting navigation: Place ID is not supported.");
return;
}
// Create a future to await the result of the asynchronous navigator task.
ListenableResultFuture<Navigator.RouteStatus> pendingRoute =
mNavigator.setDestination(destination);
// 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:
// Hide the toolbar to maximize the navigation UI.
if (getActionBar() != null) {
getActionBar().hide();
}
// Customize the navigation UI.
customizeNavigationUI();
// 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.");
break;
case NETWORK_ERROR:
displayMessage("Error starting navigation: Network error.");
break;
case ROUTE_CANCELED:
displayMessage("Error starting navigation: Route canceled.");
break;
default:
displayMessage("Error starting navigation: " + String.valueOf(code));
}
}
});
}
/** Handles the result of the request for location permissions. */
@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;
}
}
}
}
/**
* Shows a message on screen and in the log. Used when something goes wrong.
*
* @param errorMessage The message to display.
*/
private void displayMessage(String errorMessage) {
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
Log.d(TAG, errorMessage);
}
}
private GoogleMap mMap;
// Get the map, and when the async call returns, setTrafficEnabled
// (callback will be on the UI thread)
mMap = mNavFragment.getMapAsync(navMap -> navMap.setTrafficEnabled(false));
// Force night mode on.
mNavFragment.setForceNightMode(FORCE_NIGHT);
显示路线列表
首先,创建视图并将其添加到层次结构中。
void setupDirectionsListView() {
// Create the view.
DirectionsListView directionsListView = new DirectionsListView(getApplicationContext());
// Add the view to your view hierarchy.
ViewGroup group = findViewById(R.id.directions_view);
group.addView(directionsListView);
// Add a button to your layout to close the directions list view.
ImageButton button = 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));
}