這個自訂控制項位置專為自訂頁尾檢視畫面而設計。如果您看到 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 中,自訂頁尾寬度為一半寬度,並與開始側 (LTR 的左側) 對齊,就像 Nav SDK ETA 資訊卡一樣。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 配件
Navigation SDK for Android 提供 UI 配件,在導航期間會顯示類似 Google Maps for Android 應用程式中的 UI 配件。您可以按照本節所述調整這些控制項的顯示設定或外觀。您在這裡所做的變更會反映到下一個導航工作階段。
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));
}