แอปสามารถฟังเหตุการณ์ที่เกิดขึ้นขณะที่ผู้ใช้ไปยังจุดต่างๆ ตามเส้นทาง
ภาพรวม
ใช้อินเทอร์เฟซต่อไปนี้เพื่อรอเหตุการณ์การนําทาง
Navigator.ArrivalListener
มีonArrival()
การเรียกกลับ ซึ่งจะทริกเกอร์เมื่ออุปกรณ์มาถึงปลายทางRoadSnappedLocationProvider.LocationListener
มีonLocationChanged()
callback ซึ่งจะทริกเกอร์เมื่อตำแหน่งของอุปกรณ์เปลี่ยนแปลงNavigator.RemainingTimeOrDistanceChangedListener
ระบุการเรียกกลับonRemainingTimeOrDistanceChanged()
ซึ่งจะทริกเกอร์เมื่อเวลาหรือระยะทางไปยังปลายทางถัดไปเปลี่ยนแปลงมากกว่าค่าที่ระบุNavigator.RouteChangedListener
มี Callback ของonRouteChanged()
ซึ่งจะทริกเกอร์เมื่อเส้นทางเปลี่ยนแปลง
ดูรหัส
แสดง/ซ่อนโค้ด Java สำหรับกิจกรรมการนำทาง
package com.example.navsdkmultidestination; import android.content.pm.PackageManager; import android.location.Location; 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.CameraPerspective; import com.google.android.libraries.navigation.ArrivalEvent; 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.RoadSnappedLocationProvider; import com.google.android.libraries.navigation.SimulationOptions; import com.google.android.libraries.navigation.SupportNavigationFragment; import com.google.android.libraries.navigation.TimeAndDistance; import com.google.android.libraries.navigation.Waypoint; import java.util.ArrayList; import java.util.List; /** * An activity that displays a map and a navigation UI, guiding the user from their current location * to multiple destinations, also known as waypoints. */ public class NavigationActivityMultiDestination extends AppCompatActivity { private static final String TAG = NavigationActivityMultiDestination.class.getSimpleName(); private static final String DISPLAY_BOTH = "both"; private static final String DISPLAY_TOAST = "toast"; private static final String DISPLAY_LOG = "log"; private Navigator mNavigator; private RoadSnappedLocationProvider mRoadSnappedLocationProvider; private SupportNavigationFragment mNavFragment; private final List<Waypoint> mWaypoints = new ArrayList<>(); private Navigator.ArrivalListener mArrivalListener; private Navigator.RouteChangedListener mRouteChangedListener; private Navigator.RemainingTimeOrDistanceChangedListener mRemainingTimeOrDistanceChangedListener; private RoadSnappedLocationProvider.LocationListener mLocationListener; private Bundle mSavedInstanceState; private static final String KEY_JOURNEY_IN_PROGRESS = "journey_in_progress"; private boolean mJourneyInProgress = false; // Set fields for requesting location permission. private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1; private boolean mLocationPermissionGranted; /** * Sets up the navigator when the activity is created. * * @param savedInstanceState The activity state bundle. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Save the navigator state, used to determine whether a journey is in progress. mSavedInstanceState = savedInstanceState; if (mSavedInstanceState != null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS)) { mJourneyInProgress = (mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) != 0); } setContentView(R.layout.activity_main); // Initialize the Navigation SDK. initializeNavigationSdk(); } /** Releases navigation listeners when the activity is destroyed. */ @Override protected void onDestroy() { 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); } } /** Saves the state of the app when the activity is paused. */ @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mJourneyInProgress) { outState.putInt(KEY_JOURNEY_IN_PROGRESS, 1); } else { outState.putInt(KEY_JOURNEY_IN_PROGRESS, 0); } } /** * Starts the Navigation SDK and sets the camera to follow the device's location. Calls the * navigateToPlaces() 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.", DISPLAY_BOTH); 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.", DISPLAY_BOTH); mNavigator = navigator; mNavFragment = (SupportNavigationFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_fragment); // Set the camera to follow the device location with 'TILTED' driving view. mNavFragment.getMapAsync( googleMap -> googleMap.followMyLocation(CameraPerspective.TILTED)); // Navigate to the specified places. navigateToPlaces(); } /** * 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.", DISPLAY_BOTH); break; case NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED: displayMessage( "Error loading Navigation SDK: User did not accept " + "the Navigation Terms of Use.", DISPLAY_BOTH); break; case NavigationApi.ErrorCode.NETWORK_ERROR: displayMessage("Error loading Navigation SDK: Network error.", DISPLAY_BOTH); break; case NavigationApi.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); } } }); } /** Requests directions from the user's current location to a list of waypoints. */ private void navigateToPlaces() { // Set up a waypoint for each place that we want to go to. createWaypoint("ChIJq6qq6jauEmsRJAf7FjrKnXI", "Sydney Star"); createWaypoint("ChIJ3S-JXmauEmsRUcIaWtf4MzE", "Sydney Opera House"); createWaypoint("ChIJLwgLFGmuEmsRzpDhHQuyyoU", "Sydney Conservatorium of Music"); // If this journey is already in progress, no need to restart navigation. // This can happen when the user rotates the device, or sends the app to the background. if (mSavedInstanceState != null && mSavedInstanceState.containsKey(KEY_JOURNEY_IN_PROGRESS) && mSavedInstanceState.getInt(KEY_JOURNEY_IN_PROGRESS) == 1) { return; } // Create a future to await the result of the asynchronous navigator task. ListenableResultFuture<Navigator.RouteStatus> pendingRoute = mNavigator.setDestinations(mWaypoints); // 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: mJourneyInProgress = true; // Hide the toolbar to maximize the navigation UI. if (getActionBar() != null) { getActionBar().hide(); } // Register some listeners for navigation events. registerNavigationListeners(); // Display the time and distance to each waypoint. displayTimesAndDistances(); // 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.", DISPLAY_BOTH); break; case NETWORK_ERROR: displayMessage("Error starting navigation: Network error.", DISPLAY_BOTH); break; case ROUTE_CANCELED: displayMessage("Error starting navigation: Route canceled.", DISPLAY_BOTH); break; default: displayMessage("Error starting navigation: " + String.valueOf(code), DISPLAY_BOTH); } } }); } /** * Creates a waypoint from a given place ID and title. * * @param placeId The ID of the place to be converted to a waypoint. * @param title A descriptive title for the waypoint. */ private void createWaypoint(String placeId, String title) { try { mWaypoints.add(Waypoint.builder().setPlaceIdString(placeId).setTitle(title).build()); } catch (Waypoint.UnsupportedPlaceIdException e) { displayMessage( "Error starting navigation: Place ID is not supported: " + placeId, DISPLAY_BOTH); } } /** Displays the calculated travel time and distance to each waypoint. */ private void displayTimesAndDistances() { List<TimeAndDistance> timesAndDistances = mNavigator.getTimeAndDistanceList(); int leg = 1; String message = "You're on your way!"; for (TimeAndDistance timeAndDistance : timesAndDistances) { message = message + "\nRoute leg: " + leg++ + ": Travel time (seconds): " + timeAndDistance.getSeconds() + ". Distance (meters): " + timeAndDistance.getMeters(); } displayMessage(message, DISPLAY_BOTH); } /** * Registers some event listeners to show a message and take other necessary steps when specific * navigation events occur. */ private void registerNavigationListeners() { mArrivalListener = new Navigator.ArrivalListener() { @Override public void onArrival(ArrivalEvent arrivalEvent) { displayMessage( "onArrival: You've arrived at a waypoint: " + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(), DISPLAY_BOTH); // Start turn-by-turn guidance for the next leg of the route. if (arrivalEvent.isFinalDestination()) { displayMessage("onArrival: You've arrived at the final destination.", DISPLAY_BOTH); } else { mNavigator.continueToNextDestination(); mNavigator.startGuidance(); } } }; // Listens for arrival at a waypoint. mNavigator.addArrivalListener(mArrivalListener); mRouteChangedListener = new Navigator.RouteChangedListener() { @Override public void onRouteChanged() { displayMessage( "onRouteChanged: The driver's route has changed. Current waypoint: " + mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(), DISPLAY_LOG); } }; // Listens for changes in the route. mNavigator.addRouteChangedListener(mRouteChangedListener); // Listens for road-snapped location updates. mRoadSnappedLocationProvider = NavigationApi.getRoadSnappedLocationProvider(getApplication()); mLocationListener = new RoadSnappedLocationProvider.LocationListener() { @Override public void onLocationChanged(Location location) { displayMessage( "onLocationUpdated: Navigation engine has provided a new" + " road-snapped location: " + location.toString(), DISPLAY_LOG); } @Override public void onRawLocationUpdate(Location location) { 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 = new Navigator.RemainingTimeOrDistanceChangedListener() { @Override public void onRemainingTimeOrDistanceChanged() { displayMessage( "onRemainingTimeOrDistanceChanged: Time or distance estimate" + " has changed.", DISPLAY_LOG); } }; // Listens for changes in time or distance. mNavigator.addRemainingTimeOrDistanceChangedListener( 60, 100, mRemainingTimeOrDistanceChangedListener); } /** 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, String displayMedium) { 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); } } }
ตรวจหาการมาถึงจุดหมาย
โดยจุดหมายในที่นี้หมายถึงจุดหมายสุดท้ายหรือจุดแวะพัก หากต้องการตรวจหาการมาถึง ให้โทรหา Navigator.addArrivalListener()
ซึ่งจะลงทะเบียนเพื่อรับการโทรกลับเมื่ออุปกรณ์ไปถึงปลายทาง
เมื่อถึงจุดหมาย Navigation SDK สำหรับ Android จะเรียกใช้การเรียกกลับ onArrival()
และหยุดการแนะนำเส้นทางแบบเลี้ยวต่อเลี้ยว คุณต้องเรียกใช้ Navigator.continueToNextDestination()
อย่างชัดเจนเพื่อไปยังจุดถัดไป และ Navigator.startGuidance()
เพื่อนำทางแบบเลี้ยวต่อเลี้ยวต่อ
เมื่อคุณเรียกใช้ continueToNextDestination()
โปรแกรมนำทางจะทิ้งข้อมูลทั้งหมดเกี่ยวกับปลายทางก่อนหน้า หากต้องการวิเคราะห์ข้อมูลเกี่ยวกับส่วนของเส้นทางก่อนหน้า คุณต้องดึงข้อมูลจากโปรแกรมนำทางก่อนเรียกใช้ continueToNextDestination()
คุณต้องเรียกใช้ removeArrivalListener(listener)
เมื่อไม่ต้องการตัวฟังอีกต่อไปเพื่อหลีกเลี่ยงปัญหาหน่วยความจำรั่ว
mNavigator.addArrivalListener(new Navigator.ArrivalListener() {
@Override
public void onArrival(ArrivalEvent arrivalEvent) {
displayMessage("onArrival: You've arrived at a waypoint: "
+ mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
DISPLAY_BOTH);
// Start turn-by-turn guidance for the next leg of the route.
if (arrivalEvent.isFinalDestination()) {
displayMessage("onArrival: You've arrived at the final destination.",
DISPLAY_BOTH);
} else {
mNavigator.continueToNextDestination();
mNavigator.startGuidance();
}
}
});
รับการอัปเดตตำแหน่ง
รับ RoadSnappedLocationProvider
จาก
NavigationApi
แล้วโทรไปที่ RoadSnappedLocationProvider.addLocationListener()
เพื่อลงทะเบียนรับการโทรกลับเมื่อตำแหน่งหรือทิศทางของอุปกรณ์เปลี่ยนแปลง โปรดทราบว่าตำแหน่งนี้มีการจับคู่กับถนน จึงอาจแตกต่างจากตำแหน่งที่ผู้ให้บริการตำแหน่งแบบรวมแสดงใน API ตำแหน่งของบริการ Google Play
Navigation SDK จะอัปเดตตำแหน่งให้บ่อยที่สุด เมื่อการอัปเดตตำแหน่งพร้อมใช้งาน Navigation SDK จะเรียกใช้การเรียกกลับ onLocationChanged()
การอัปเดตตำแหน่งที่จับคู่กับถนนจะไม่ขึ้นอยู่กับการนำทาง และสามารถทำงานต่อไปได้แม้ว่าการนำทางจะหยุดแล้วก็ตาม การสมัครรับการอัปเดตตำแหน่งอาจทำให้แบตเตอรี่หมด หน่วยความจำรั่ว หรือมีการรวบรวมข้อมูลตำแหน่งของอุปกรณ์โดยไม่ตั้งใจหากคุณปล่อยให้การอัปเดตตำแหน่งทำงานอยู่เบื้องหลัง โทรไปที่ RoadSnappedLocationProvider.removeLocationListener
เมื่อไม่ต้องการฟังเสียงอีกต่อไป
mRoadSnappedLocationProvider =
NavigationApi.getRoadSnappedLocationProvider(getApplication());
if (mRoadSnappedLocationProvider != null) {
mRoadSnappedLocationProvider.addLocationListener(
new RoadSnappedLocationProvider.LocationListener() {
@Override
public void onLocationChanged(Location location) {
displayMessage("onLocationUpdated: Navigation engine has provided a new"
+ " road-snapped location: "
+ location.toString(),
DISPLAY_LOG);
}
});
} else {
displayMessage("ERROR: Failed to get a location provider", DISPLAY_LOG);
}
รับข้อมูลอัปเดตเวลาและระยะทาง
Call
Navigator.addRemainingTimeOrDistanceChangedListener()
เพื่อลงทะเบียนรับการโทรกลับเมื่อเวลาที่เหลือ (วินาที) หรือระยะทาง (เมตร) เปลี่ยนแปลงมากกว่าเกณฑ์ที่กำหนด
เมื่อการเปลี่ยนแปลงเวลาหรือระยะทางเกินกว่าจำนวนที่ระบุไว้ Navigation SDK จะเรียกใช้การเรียกคืน onRemainingTimeOrDistanceChanged()
หากต้องการดูเวลาและระยะทางที่เหลือ ให้โทรไปที่ Navigator.getTimeAndDistanceList()
โปรดทราบว่าเวลาและระยะทางในรายการจะรวมกัน โดยจะแสดงเวลาและระยะทางจากตำแหน่งปัจจุบันไปยังจุดสังเกตแต่ละจุด ไม่ใช่จากจุดสังเกตหนึ่งไปยังอีกจุดหนึ่ง
ตอนนี้ออบเจ็กต์ TimeAndDistance
จะแสดงผล delaySeverity
ด้วย ซึ่งจะเป็นค่าหนัก ปานกลาง เบา หรือไม่ทราบ ซึ่งสอดคล้องกับสีที่คุณเห็นใน ETA ใน UI ของ Maps (มาก = สีแดง ปานกลาง = สีเหลือง น้อย = สีเขียว) ซึ่งมีประโยชน์ในกรณีที่คุณต้องสร้างส่วนท้ายเวลาถึงโดยประมาณของคุณเอง
คุณต้องเรียกใช้ Navigator.removeRemainingTimeOrDistanceChangedListener(listener)
เมื่อไม่ต้องการตัวฟังอีกต่อไปเพื่อหลีกเลี่ยงปัญหาหน่วยความจำรั่ว
ตัวอย่างด้านล่างจะขอการติดต่อกลับหากเวลาที่เหลือเปลี่ยนแปลงมากกว่า 60 วินาทีหรือระยะทางที่เหลือเปลี่ยนแปลงมากกว่า 100 เมตร
mNavigator.addRemainingTimeOrDistanceChangedListener(60, 100,
new Navigator.RemainingTimeOrDistanceChangedListener() {
@Override
public void onRemainingTimeOrDistanceChanged() {
displayMessage("onRemainingTimeOrDistanceChanged: Time or distance estimate"
+ " has changed.",
DISPLAY_LOG);
}
});
คุณสามารถแสดงข้อมูลเวลาและระยะทางที่เหลือได้โดยใช้จอแสดงผลในตัวโดยใช้เมธอด setEtaCardEnabled()
และส่งพารามิเตอร์ที่มีค่า TRUE
หากต้องการซ่อนการแสดงเวลาและระยะทาง ให้ตั้งค่านี้เป็น FALSE
นอกจากนี้ คุณยังแสดงเวลาถึงจุดหมายโดยประมาณสำหรับจุดแวะพักหลายจุดได้โดยใช้เมธอด getTimeAndDistanceList()
รับข้อมูลอัปเดตเส้นทาง
โทรหา Navigator.addRouteChangedListener()
เพื่อลงทะเบียนรับการติดต่อกลับเมื่อเส้นทางเปลี่ยนแปลง
เมื่อเส้นทางเปลี่ยนแปลง Navigation SDK จะเรียกใช้การเรียกกลับ onRouteChanged()
คุณสามารถโทรไปที่ Navigator.getRouteSegments
และ Navigator.getCurrentRouteSegment()
เพื่อค้นหาเส้นทางใหม่
คุณต้องเรียกใช้ removeRouteChangedListener(listener)
เมื่อไม่ต้องการตัวฟังอีกต่อไปเพื่อหลีกเลี่ยงปัญหาหน่วยความจำรั่ว
mNavigator.addRouteChangedListener(new Navigator.RouteChangedListener() {
@Override
public void onRouteChanged() {
displayMessage("onRouteChanged: The driver's route has changed. Current waypoint: "
+ mNavigator.getCurrentRouteSegment().getDestinationWaypoint().getTitle(),
DISPLAY_LOG);
}
});
ตรวจหาเมื่อโหมดกลางคืนเปลี่ยนแปลง
โทรไปที่ NavigationView.addOnNightModeChangedListener
หรือ SupportNavigationFragment.addOnNightModeChangedListener
เพื่อลงทะเบียนรับการติดต่อกลับเมื่อมีการเปลี่ยนแปลงโหมดกลางคืน
ตัวอย่างต่อไปนี้แสดงการฟังการเปลี่ยนแปลงโหมดกลางคืนในฟрагเมนต์การนำทาง
mNavFragment.addOnNightModeChangedListener(new NavigationView.OnNightModeChangedListener() {
@Override
public void onNightModeChanged(NightModeChangedEvent nightModeChangedEvent) {
displayMessage("Night mode is active: " + nightModeChangedEvent.inNightMode(),
DISPLAY_LOG);
}
});
นอกจากนี้ คุณยังตั้งค่าโหมดกลางคืนแบบเป็นโปรแกรมได้ด้วย ดูข้อมูลเพิ่มเติมได้ที่ตั้งค่าโหมดกลางคืน