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

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

סיכום

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

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

הצגת הקוד

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

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

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. משנים את פונקציית ה-callback של 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;
                }
            }
        }
    }
    

איך מאתחלים את Navigation SDK

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

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

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

  3. אופציונלי. הפעלת הגבלות דרכים במדינות נתמכות. מגדירים את הספרה האחרונה של לוחית הרישוי. צריך לבצע את הקריאה הזו רק פעם אחת: המערכת תמשיך להשתמש בה בבקשות הבאות לקבלת מסלול. הקריאה הזו פועלת רק באזורים נתמכים. המדינות הנתמכות ב-Navigation SDK

    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 להרצת אפליקציות במכשיר חומרה. לחלופין, אפשר להגדיר מכשיר וירטואלי באמצעות מנהל המכשירים הווירטואליים של Android‏ (AVD). כשבוחרים אמולטור, חשוב לבחור קובץ אימג' שכולל את ממשקי Google API.
  2. ב-Android Studio, לוחצים על אפשרות התפריט הפעלה או על סמל לחצן ההפעלה. בוחרים מכשיר לפי ההנחיות.

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

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