ניווט במסלול עם יעד אחד

ניתן להיעזר במדריך הזה כדי להציג מסלול בתוך האפליקציה באמצעות ה-SDK של הניווט ל-Android. המדריך הזה מתבסס על ההנחה שכבר שילבתם את ה-SDK של הניווט באפליקציה, כפי שמתואר במאמר הגדרת הפרויקט.

סיכום

  1. אפשר להוסיף לאפליקציה רכיב של ממשק משתמש, כקטע ניווט או כתצוגת ניווט. הרכיב הזה בממשק המשתמש מוסיף לפעילות את המפה האינטראקטיבית ואת ממשק המשתמש לניווט לפי מסלול מפורט.
  2. צריך לבקש הרשאות מיקום. האפליקציה שלכם חייבת לבקש הרשאת מיקום כדי לקבוע את מיקום המכשיר.
  3. מפעילים את ה-SDK באמצעות המחלקה NavigationApi.
  4. הגדר יעד ולשלוט בניווט המפורט באמצעות המחלקה Navigator. התהליך כולל שלושה שלבים:

    • הגדרת היעד באמצעות setDestination().
    • מתחילים את הניווט באמצעות startGuidance().
    • שימוש ב-getSimulator() כדי לדמות את התקדמות הרכב לאורך המסלול, לצורך בדיקה, ניפוי באגים והדגמה של האפליקציה.
  5. בונים ומפעילים את האפליקציה.

להצגת הקוד

הוספת רכיב של ממשק משתמש לאפליקציה

בסעיף זה מפורטות שתי דרכים להוספת המפה האינטראקטיבית וממשק המשתמש לצורך הצגת ניווט במסלול מפורט.

SupportNavigationFragment הוא רכיב ממשק המשתמש שמציג את הפלט החזותי של הניווט, כולל מפה אינטראקטיבית ומסלול מפורט. תוכלו להצהיר על המקטע בקובץ פריסת ה-XML כפי שמוצג כאן:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.google.android.libraries.navigation.SupportNavigationFragment"
    android:id="@+id/navigation_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

לחלופין, אפשר ליצור את המקטע באופן פרוגרמטי, כפי שמתואר במסמכי התיעוד של Android, באמצעות FragmentActivity.getSupportFragmentManager().

כחלופה למקטע, הרכיב של ממשק המשתמש להצגת מפה לניווט זמין גם כ-NavigationView.

שליחת בקשה להרשאת מיקום

בקטע הזה מוסבר איך לבקש הרשאת מיקום מדויקת. לפרטים נוספים, עיינו במדריך להרשאות ב-Android.

  1. מוסיפים את ההרשאה כצאצא של הרכיב <manifest> במניפסט של Android:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.navsdksingledestination">
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    </manifest>
    
  2. צריך לבקש הרשאות בזמן ריצה באפליקציה, כדי לתת למשתמש את האפשרות להעניק או לדחות הרשאת מיקום. הקוד הבא בודק אם המשתמש העניק הרשאת מיקום מדויקת. אם לא, תוצג בקשת ההרשאה:

    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;
    }
    
  3. כדי לטפל בתוצאה של בקשת ההרשאה, מבטלים את הקריאה החוזרת של onRequestPermissionsResult():

    @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;
                }
            }
        }
    }
    

אתחל את ה-SDK לניווט

המחלקה NavigationApi מספקת לוגיקת אתחול שמאפשרת לאפליקציה להשתמש בניווט של Google. סעיף זה מסביר איך לאתחל את הניווט, יחד עם כמה הגדרות נוספות שאפשר להפעיל עבור האפליקציה:

  1. מפעילים את ה-SDK של הניווט ומבטלים את הקריאה החוזרת (callback) של onNavigatorReady() כדי להתחיל את הניווט כשהניווט מוכן.

  2. אפשרות. צריך להגדיר את האפליקציה כך שהתראות ההנחיות ושירותי הרקע יכבו כשהמשתמש יסגור את האפליקציה מהמכשיר. הבחירה הזו תלויה במודל העסקי שלכם. יכול להיות שתרצו להשתמש בהתנהגות ברירת המחדל של כלי הניווט, שממשיכה להציג הנחיות פנייה ועדכוני מיקום גם אחרי שסוגרים את האפליקציה. אם רוצים להשבית את הניווט ואת עדכוני המיקום אחרי שמשתמש הקצה סוגר את האפליקציה, צריך להשתמש בהגדרה הזו.

  3. אפשרות. הפעלת הגבלות כבישים במדינות נתמכות. מגדירים את הספרה האחרונה בלוחית הרישוי. צריך לבצע את השיחה הזו רק פעם אחת: הבקשות הבאות לקבלת מסלול ימשיכו להשתמש בה. השיחה הזו פועלת רק באזורים נתמכים. ראו Countries and regions supported for two-wheeled vehicles.

    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 = (NavigationFragment) getFragmentManager()
                            .findFragmentById(R.id.navigation_fragment);
    
                    // Optional. Disable the guidance notifications and shut down the app
                    // and background service when the user closes the app.
                    // mNavigator.setTaskRemovedBehavior(Navigator.TaskRemovedBehavior.QUIT_SERVICE)
    
                    // Optional. Set the last digit of the car's license plate to get
                    // route restrictions for supported countries.
                    // mNavigator.setLicensePlateRestrictionInfo(getLastDigit(), "BZ");
    
                    // Set the camera to follow the device location with 'TILTED' driving view.
                    mNavFragment.getCamera().followMyLocation(Camera.Perspective.TILTED);
    
                    // Set the travel mode (DRIVING, WALKING, CYCLING, TWO_WHEELER, or TAXI).
                    mRoutingOptions = new RoutingOptions();
                    mRoutingOptions.travelMode(RoutingOptions.TravelMode.DRIVING);
    
                    // Navigate to a place, specified by Place ID.
                    navigateToPlace(SYDNEY_OPERA_HOUSE, mRoutingOptions);
                }
    
                /**
                 * 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);
                    }
                }
            });
    

הגדרת יעד

המחלקה Navigator מספקת שליטה על ההגדרה, ההתחלה והעצירה של מסע הניווט.

בעזרת ה-Navigator שקיבלתם בסעיף הקודם, מגדירים את היעד Waypoint למסע הזה. אחרי חישוב המסלול, ב-SupportNavigationFragment מוצג קו פוליגוני שמייצג את המסלול במפה, וסמן ביעד.

    private void navigateToPlace(String placeId, RoutingOptions travelMode) {
        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, travelMode);

        // 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();
                                }

                                // 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));
                        }
                    }
                });
    }

פיתוח והרצה של האפליקציה

  1. מחברים מכשיר Android למחשב. פועלים לפי ההוראות ב-Android Studio להפעלת אפליקציות במכשיר חומרה. לחלופין, אפשר להגדיר מכשיר וירטואלי באמצעות מנהל המכשיר הווירטואלי (AVD) של Android. כשבוחרים אמולטור, חשוב לבחור תמונה שכוללת את Google APIs.
  2. ב-Android Studio, לוחצים על האפשרות בתפריט הפעלה או על סמל לחצן ההפעלה. בוחרים מכשיר לפי ההוראות.

רמזים לשיפור חוויית המשתמש

  • המשתמש חייב לאשר את התנאים וההגבלות של הניווט של Google כדי שהניווט יהיה זמין. האישור הזה נדרש פעם אחת בלבד. כברירת מחדל, ה-SDK מבקש אישור בפעם הראשונה שהניווט מופעל. אם תעדיפו, תוכלו להפעיל את תיבת הדו-שיח של התנאים וההגבלות בנושא ניווט בשלב מוקדם בתהליך חוויית המשתמש באפליקציה, למשל במהלך ההרשמה או ההתחברות, באמצעות TermsAndConditionsCheckOption.
  • כדי לשפר משמעותית את איכות הניווט ואת הדיוק של זמן ההגעה המשוער, כדאי להשתמש במזהי מקומות כדי לאתחל ציוני דרך במקום קואורדינטות של קווי אורך ורוחב.
  • הדוגמה הזו מפיקה את ציון הדרך של היעד ממזהה מקום ספציפי של בית האופרה של סידני. אפשר להשתמש במאתר מזהי המקומות כדי לקבל מזהי מקומות של מיקומים ספציפיים אחרים.