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

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

טופס כתובת שמוצגות בו הצעות להשלמה אוטומטית של מקומות ושדות שמולאו

סרטונים: שיפור טופסי כתובות באמצעות השלמה אוטומטית למקומות

טפסים של כתובות

Android

iOS

פיתוח אתרים

Google Maps Platform מספקת ווידג'ט של השלמה אוטומטית למקומות לפלטפורמות לנייד ולאינטרנט. הווידג'ט, שמוצג באיורים הקודמים, מספק תיבת דו-שיח לחיפוש עם פונקציונליות מובנית של השלמה אוטומטית, שאפשר אפילו לבצע לה אופטימיזציה לחיפוש לפי מיקום.

קבל את הקוד

משכפלים או מורידים את מאגר ההדגמות של Google Places SDK ל-Android מ-GitHub.

הצגת גרסת ה-Java של הפעילות:

    /*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.placesdemo;

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static com.google.maps.android.SphericalUtil.computeDistanceBetween;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewStub;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.example.placesdemo.databinding.AutocompleteAddressActivityBinding;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.AddressComponent;
import com.google.android.libraries.places.api.model.AddressComponents;
import com.google.android.libraries.places.api.model.AutocompletePrediction;
import com.google.android.libraries.places.api.model.AutocompleteSessionToken;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceTypes;
import com.google.android.libraries.places.api.net.FetchPlaceRequest;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.libraries.places.widget.PlaceAutocomplete;
import java.util.Arrays;
import java.util.List;

/** Activity for using Place Autocomplete to assist filling out an address form. */
@SuppressWarnings("FieldCanBeLocal")
public class AutocompleteAddressActivity extends AppCompatActivity implements OnMapReadyCallback {

    private static final String TAG = "ADDRESS_AUTOCOMPLETE";
    private static final String MAP_FRAGMENT_TAG = "MAP";
    private LatLng coordinates;
    private boolean checkProximity = false;
    private SupportMapFragment mapFragment;
    private GoogleMap map;
    private Marker marker;
    private PlacesClient placesClient;
    private View mapPanel;
    private LatLng deviceLocation;
    private static final double acceptedProximity = 150;

    private AutocompleteAddressActivityBinding binding;

    View.OnClickListener startAutocompleteIntentListener =
            view -> {
                view.setOnClickListener(null);
                startAutocompleteIntent();
            };

    private final ActivityResultLauncher<Intent> startAutocomplete =
            registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Intent intent = result.getData();
                            if (intent != null) {
                                AutocompletePrediction prediction =
                                        PlaceAutocomplete.getPredictionFromIntent(intent);
                                AutocompleteSessionToken sessionToken =
                                        PlaceAutocomplete.getSessionTokenFromIntent(intent);
                                if (prediction != null) {
                                    List<Place.Field> fields =
                                            Arrays.asList(
                                                    Place.Field.ADDRESS_COMPONENTS,
                                                    Place.Field.LOCATION,
                                                    Place.Field.VIEWPORT);
                                    FetchPlaceRequest request =
                                            FetchPlaceRequest.builder(
                                                            prediction.getPlaceId(), fields)
                                                    .setSessionToken(sessionToken)
                                                    .build();
                                    placesClient
                                            .fetchPlace(request)
                                            .addOnSuccessListener(
                                                    response -> {
                                                        Place place = response.getPlace();
                                                        // Write a method to read the address
                                                        // components from the Place
                                                        // and populate the form with the address
                                                        // components
                                                        Log.d(
                                                                TAG,
                                                                "Place: "
                                                                        + place
                                                                                .getAddressComponents());
                                                        fillInAddress(place);
                                                    })
                                            .addOnFailureListener(
                                                    exception -> {
                                                        Log.e(
                                                                TAG,
                                                                "Place not found: "
                                                                        + exception.getMessage());
                                                    });
                                }
                            }
                        } else if (result.getResultCode() == Activity.RESULT_CANCELED) {
                            // The user canceled the operation.
                            Log.i(TAG, "User canceled autocomplete");
                        }
                    });


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        binding.autocompleteAddress1.setOnClickListener(startAutocompleteIntentListener);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Enable edge-to-edge display. This must be called before calling super.onCreate().
        EdgeToEdge.enable(this);
        super.onCreate(savedInstanceState);

        binding = AutocompleteAddressActivityBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Retrieve a PlacesClient (previously initialized - see MainActivity)
        placesClient = Places.createClient(this);

        // Attach an Autocomplete intent to the Address 1 EditText field
        binding.autocompleteAddress1.setOnClickListener(startAutocompleteIntentListener);

        // Update checkProximity when user checks the checkbox
        CheckBox checkProximityBox = findViewById(R.id.checkbox_proximity);
        checkProximityBox.setOnCheckedChangeListener(
                (view, isChecked) -> {
                    // Set the boolean to match user preference for when the Submit button is
                    // clicked
                    checkProximity = isChecked;
                });

        // Submit and optionally check proximity
        Button saveButton = findViewById(R.id.autocomplete_save_button);
        saveButton.setOnClickListener(v -> saveForm());

        // Reset the form
        Button resetButton = findViewById(R.id.autocomplete_reset_button);
        resetButton.setOnClickListener(v -> clearForm());
    }

    private void startAutocompleteIntent() {
        // Build the autocomplete intent with country and type filters applied
        Intent intent =
                new PlaceAutocomplete.IntentBuilder()
                        .setCountries(List.of("US"))
                        .setTypesFilter(List.of(PlaceTypes.ADDRESS))
                        .build(this);
        startAutocomplete.launch(intent);
    }


    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        map = googleMap;
        try {
            // Customise the styling of the base map using a JSON object defined
            // in a string resource.
            boolean success =
                    map.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.style_json));

            if (!success) {
                Log.e(TAG, "Style parsing failed.");
            }
        } catch (Resources.NotFoundException e) {
            Log.e(TAG, "Can't find style. Error: ", e);
        }
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(coordinates, 15f));
        marker = map.addMarker(new MarkerOptions().position(coordinates));
    }


    private void fillInAddress(Place place) {
        AddressComponents components = place.getAddressComponents();
        StringBuilder address1 = new StringBuilder();
        StringBuilder postcode = new StringBuilder();

        // Get each component of the address from the place details,
        // and then fill-in the corresponding field on the form.
        // Possible AddressComponent types are documented at https://goo.gle/32SJPM1
        if (components != null) {
            for (AddressComponent component : components.asList()) {
                String type = component.getTypes().get(0);
                switch (type) {
                    case "street_number":
                        {
                            address1.insert(0, component.getName());
                            break;
                        }

                    case "route":
                        {
                            address1.append(" ");
                            address1.append(component.getShortName());
                            break;
                        }

                    case "postal_code":
                        {
                            postcode.insert(0, component.getName());
                            break;
                        }

                    case "postal_code_suffix":
                        {
                            postcode.append("-").append(component.getName());
                            break;
                        }

                    case "locality":
                        binding.autocompleteCity.setText(component.getName());
                        break;

                    case "administrative_area_level_1":
                        {
                            binding.autocompleteState.setText(component.getShortName());
                            break;
                        }

                    case "country":
                        binding.autocompleteCountry.setText(component.getName());
                        break;
                }
            }
        }

        binding.autocompleteAddress1.setText(address1.toString());
        binding.autocompletePostal.setText(postcode.toString());

        // After filling the form with address components from the Autocomplete
        // prediction, set cursor focus on the second address line to encourage
        // entry of sub-premise information such as apartment, unit, or floor number.
        binding.autocompleteAddress2.requestFocus();

        // Add a map for visual confirmation of the address
        showMap(place);
    }

    private void showMap(Place place) {
        coordinates = place.getLocation();

        // It isn't possible to set a fragment's id programmatically so we set a tag instead and
        // search for it using that.
        mapFragment =
                (SupportMapFragment)
                        getSupportFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);

        // We only create a fragment if it doesn't already exist.
        if (mapFragment == null) {
            mapPanel = ((ViewStub) findViewById(R.id.stub_map)).inflate();
            GoogleMapOptions mapOptions = new GoogleMapOptions();
            mapOptions.mapToolbarEnabled(false);

            // To programmatically add the map, we first create a SupportMapFragment.
            mapFragment = SupportMapFragment.newInstance(mapOptions);

            // Then we add it using a FragmentTransaction.
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.confirmation_map, mapFragment, MAP_FRAGMENT_TAG)
                    .commit();
            mapFragment.getMapAsync(this);
        } else {
            updateMap(coordinates);
        }
    }


    private void updateMap(LatLng latLng) {
        marker.setPosition(latLng);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f));
        if (mapPanel.getVisibility() == View.GONE) {
            mapPanel.setVisibility(View.VISIBLE);
        }
    }

    private void saveForm() {
        Log.d(TAG, "checkProximity = " + checkProximity);
        if (checkProximity) {
            checkLocationPermissions();
        } else {
            Toast.makeText(this, R.string.autocomplete_skipped_message, Toast.LENGTH_SHORT).show();
        }
    }

    private void clearForm() {
        binding.autocompleteAddress1.setText("");
        binding.autocompleteAddress2.getText().clear();
        binding.autocompleteCity.getText().clear();
        binding.autocompleteState.getText().clear();
        binding.autocompletePostal.getText().clear();
        binding.autocompleteCountry.getText().clear();
        if (mapPanel != null) {
            mapPanel.setVisibility(View.GONE);
        }
        binding.autocompleteAddress1.requestFocus();
    }

    // Register the permissions callback, which handles the user's response to the
    // system permissions dialog. Save the return value, an instance of
    // ActivityResultLauncher, as an instance variable.
    private final ActivityResultLauncher<String> requestPermissionLauncher =
            registerForActivityResult(
                    new ActivityResultContracts.RequestPermission(),
                    isGranted -> {
                        if (isGranted) {
                            // Since ACCESS_FINE_LOCATION is the only permission in this sample,
                            // run the location comparison task once permission is granted.
                            // Otherwise, check which permission is granted.
                            getAndCompareLocations();
                        } else {
                            // Fallback behavior if user denies permission
                            Log.d(TAG, "User denied permission");
                        }
                    });


    private void checkLocationPermissions() {
        if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            getAndCompareLocations();
        } else {
            requestPermissionLauncher.launch(ACCESS_FINE_LOCATION);
        }
    }


    @SuppressLint("MissingPermission")
    private void getAndCompareLocations() {
        // TODO: Detect and handle if user has entered or modified the address manually and update
        // the coordinates variable to the Lat/Lng of the manually entered address. May use
        // Geocoding API to convert the manually entered address to a Lat/Lng.
        LatLng enteredLocation = coordinates;
        map.setMyLocationEnabled(true);

        FusedLocationProviderClient fusedLocationClient =
                LocationServices.getFusedLocationProviderClient(this);

        fusedLocationClient
                .getLastLocation()
                .addOnSuccessListener(
                        this,
                        location -> {
                            // Got last known location. In some rare situations this can be null.
                            if (location == null) {
                                return;
                            }

                            deviceLocation =
                                    new LatLng(location.getLatitude(), location.getLongitude());
                            Log.d(TAG, "device location = " + deviceLocation);
                            Log.d(TAG, "entered location = " + enteredLocation.toString());

                            // Use the computeDistanceBetween function in the Maps SDK for Android
                            // Utility Library
                            // to use spherical geometry to compute the distance between two Lat/Lng
                            // points.
                            double distanceInMeters =
                                    computeDistanceBetween(deviceLocation, enteredLocation);
                            if (distanceInMeters <= acceptedProximity) {
                                Log.d(TAG, "location matched");
                                // TODO: Display UI based on the locations matching
                            } else {
                                Log.d(TAG, "location not matched");
                                // TODO: Display UI based on the locations not matching
                            }
                        });
    }
}

    

הפעלת ממשקי API

כדי ליישם את ההמלצות האלה, צריך להפעיל את ממשקי ה-API הבאים במסוף Google Cloud:

מידע נוסף על ההגדרה זמין במאמר הגדרת פרויקט בענן ב-Google Cloud.

הוספת השלמה אוטומטית לשדות להזנת קלט

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

הוספת הווידג'ט "השלמה אוטומטית למקומות"

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

קודם כל, מכינים מרכז פעילויות באמצעות ActivityResultLauncher, שיאזין לתוצאה מהפעילות שהופעלה. התוצאה של פונקציית ה-callback תכיל אובייקט Place שתואם לכתובת שהמשתמש בחר מתוך ההצעות להשלמה האוטומטית.

    private final ActivityResultLauncher<Intent> startAutocomplete =
            registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Intent intent = result.getData();
                            if (intent != null) {
                                AutocompletePrediction prediction =
                                        PlaceAutocomplete.getPredictionFromIntent(intent);
                                AutocompleteSessionToken sessionToken =
                                        PlaceAutocomplete.getSessionTokenFromIntent(intent);
                                if (prediction != null) {
                                    List<Place.Field> fields =
                                            Arrays.asList(
                                                    Place.Field.ADDRESS_COMPONENTS,
                                                    Place.Field.LOCATION,
                                                    Place.Field.VIEWPORT);
                                    FetchPlaceRequest request =
                                            FetchPlaceRequest.builder(
                                                            prediction.getPlaceId(), fields)
                                                    .setSessionToken(sessionToken)
                                                    .build();
                                    placesClient
                                            .fetchPlace(request)
                                            .addOnSuccessListener(
                                                    response -> {
                                                        Place place = response.getPlace();
                                                        // Write a method to read the address
                                                        // components from the Place
                                                        // and populate the form with the address
                                                        // components
                                                        Log.d(
                                                                TAG,
                                                                "Place: "
                                                                        + place
                                                                                .getAddressComponents());
                                                        fillInAddress(place);
                                                    })
                                            .addOnFailureListener(
                                                    exception -> {
                                                        Log.e(
                                                                TAG,
                                                                "Place not found: "
                                                                        + exception.getMessage());
                                                    });
                                }
                            }
                        } else if (result.getResultCode() == Activity.RESULT_CANCELED) {
                            // The user canceled the operation.
                            Log.i(TAG, "User canceled autocomplete");
                        }
                    });

בשלב הבא, מגדירים את השדות, המיקום ומאפייני הסוג של ה-Intent של ההשלמה האוטומטית למקומות, ויוצרים אותו באמצעות Autocomplete.IntentBuilder. לבסוף, מפעילים את ה-Intent באמצעות ActivityResultLauncher שהוגדר בדוגמת הקוד הקודמת.

    private void startAutocompleteIntent() {
        // Build the autocomplete intent with country and type filters applied
        Intent intent =
                new PlaceAutocomplete.IntentBuilder()
                        .setCountries(List.of("US"))
                        .setTypesFilter(List.of(PlaceTypes.ADDRESS))
                        .build(this);
        startAutocomplete.launch(intent);
    }

טיפול בכתובת שמוחזרת על ידי השלמה אוטומטית למקומות

הגדרת ActivityResultLauncher קודם לכן הגדירה גם מה צריך לעשות כשתוצאת הפעילות מוחזרת בקריאה החוזרת (callback). אם המשתמש בחר חיזוי, הוא יועבר ב-intent שכלול באובייקט התוצאה. מכיוון שה-Intent נוצר על ידי Autocomplete.IntentBuilder, השיטה Autocomplete.getPlaceFromIntent() יכולה לחלץ ממנו את האובייקט Place.

    private final ActivityResultLauncher<Intent> startAutocomplete =
            registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Intent intent = result.getData();
                            if (intent != null) {
                                AutocompletePrediction prediction =
                                        PlaceAutocomplete.getPredictionFromIntent(intent);
                                AutocompleteSessionToken sessionToken =
                                        PlaceAutocomplete.getSessionTokenFromIntent(intent);
                                if (prediction != null) {
                                    List<Place.Field> fields =
                                            Arrays.asList(
                                                    Place.Field.ADDRESS_COMPONENTS,
                                                    Place.Field.LOCATION,
                                                    Place.Field.VIEWPORT);
                                    FetchPlaceRequest request =
                                            FetchPlaceRequest.builder(
                                                            prediction.getPlaceId(), fields)
                                                    .setSessionToken(sessionToken)
                                                    .build();
                                    placesClient
                                            .fetchPlace(request)
                                            .addOnSuccessListener(
                                                    response -> {
                                                        Place place = response.getPlace();
                                                        // Write a method to read the address
                                                        // components from the Place
                                                        // and populate the form with the address
                                                        // components
                                                        Log.d(
                                                                TAG,
                                                                "Place: "
                                                                        + place
                                                                                .getAddressComponents());
                                                        fillInAddress(place);
                                                    })
                                            .addOnFailureListener(
                                                    exception -> {
                                                        Log.e(
                                                                TAG,
                                                                "Place not found: "
                                                                        + exception.getMessage());
                                                    });
                                }
                            }
                        } else if (result.getResultCode() == Activity.RESULT_CANCELED) {
                            // The user canceled the operation.
                            Log.i(TAG, "User canceled autocomplete");
                        }
                    });

משם, קוראים ל-Place.getAddressComponents() ומתאימים כל רכיב של הכתובת לשדה להזנת קלט המתאים בטופס הכתובת, ומאכלסים את השדה בערך מהמקום שהמשתמש בחר.

דוגמה להטמעה של אכלוס שדות הטופס של הכתובת משותפת ב-method‏ fillInAddress של הקוד לדוגמה שמופיע בקטע קבלת הקוד בדף הזה.

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

שיקולים בהטמעה של השלמה אוטומטית למקומות

להשלמה האוטומטית למקומות יש כמה אפשרויות שמאפשרות גמישות בהטמעה, אם אתם רוצים להשתמש ביותר מהווידג'ט בלבד. אתם יכולים להשתמש בשילוב של שירותים כדי לקבל בדיוק את מה שאתם צריכים כדי להתאים מיקום בצורה הנכונה.

  • בטופס ADDRESS, מגדירים את הפרמטר types לערך address כדי להגביל את ההתאמות לכתובות רחוב מלאות. מידע נוסף על סוגים שנתמכים בבקשות להשלמה אוטומטית למקומות

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

    • משתמשים ב-RectangularBounds כדי להגדיר את הגבולות המלבניים להגבלת אזור, ומשתמשים ב-setLocationRestriction() כדי לוודא שיוחזרו רק כתובות באזורים האלה.

    • אפשר להשתמש במדיניות setCountries() כדי להגביל את התשובות לקבוצה מסוימת של מדינות.

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

אישור חזותי של הכתובת

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

בתמונה הבאה מוצגת מפה מתחת לכתובת, עם סיכה בכתובת שהוזנה.

טופס כתובת עם סמן במפה שמאשר את המיקום שנבחר

הדוגמה הבאה מבוססת על השלבים הבסיסיים להוספת מפה ב-Android. פרטים נוספים מופיעים במסמכי התיעוד.

מתבצעת הוספה של SupportMapFragment

קודם מוסיפים קטע קוד של SupportMapFragment לקובץ ה-XML של הפריסה.

    <fragment
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:id="@+id/confirmation_map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

לאחר מכן, מוסיפים את הקטע באופן פרוגרמטי אם הוא עדיין לא קיים.

    private void showMap(Place place) {
        coordinates = place.getLocation();

        // It isn't possible to set a fragment's id programmatically so we set a tag instead and
        // search for it using that.
        mapFragment =
                (SupportMapFragment)
                        getSupportFragmentManager().findFragmentByTag(MAP_FRAGMENT_TAG);

        // We only create a fragment if it doesn't already exist.
        if (mapFragment == null) {
            mapPanel = ((ViewStub) findViewById(R.id.stub_map)).inflate();
            GoogleMapOptions mapOptions = new GoogleMapOptions();
            mapOptions.mapToolbarEnabled(false);

            // To programmatically add the map, we first create a SupportMapFragment.
            mapFragment = SupportMapFragment.newInstance(mapOptions);

            // Then we add it using a FragmentTransaction.
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.confirmation_map, mapFragment, MAP_FRAGMENT_TAG)
                    .commit();
            mapFragment.getMapAsync(this);
        } else {
            updateMap(coordinates);
        }
    }

קבלת נקודת אחיזה לפרגמנט ורישום הקריאה החוזרת (callback)

  1. כדי לקבל נקודת אחיזה למקטע, צריך לבצע קריאה ל-method‏ FragmentManager.findFragmentById ולהעביר לה את מזהה המשאב של המקטע בקובץ הפריסה. אם הוספתם את המקטע באופן דינמי, דלגו על השלב הזה כי כבר אחזרתם את נקודת האחיזה.

  2. מפעילים את ה-method‏ getMapAsync כדי להגדיר את הקריאה החוזרת (callback) במקטע.

לדוגמה, אם הוספתם את הקטע באופן סטטי:

Kotlin

val mapFragment = supportFragmentManager
    .findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)

      

Java

SupportMapFragment mapFragment =
        (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);

      

עיצוב והוספה של סמן למפה

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

    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        map = googleMap;
        try {
            // Customise the styling of the base map using a JSON object defined
            // in a string resource.
            boolean success =
                    map.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.style_json));

            if (!success) {
                Log.e(TAG, "Style parsing failed.");
            }
        } catch (Resources.NotFoundException e) {
            Log.e(TAG, "Can't find style. Error: ", e);
        }
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(coordinates, 15f));
        marker = map.addMarker(new MarkerOptions().position(coordinates));
    }

(לדוגמת הקוד המלאה)

השבתת הכלים לעריכת המפה

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