סמנים

בחירת פלטפורמה: Android iOS JavaScript

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

דוגמאות קוד

המאגר ApiDemos ב-GitHub כולל דוגמה שממחישה תכונות שונות של סמנים:

Kotlin

Java

מבוא

סמנים מזהים מיקומים במפה. הסמן שמוגדר כברירת מחדל כולל סמל רגיל, שמאפיין את המראה והתחושה של מפות Google. אפשר לשנות את הצבע, התמונה או נקודת העיגון של הסמל דרך ה-API. סמנים הם אובייקטים מסוג Marker, והם מתווספים למפה באמצעות השיטה GoogleMap.addMarker(markerOptions).

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

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

תחילת העבודה עם סמנים

בפרק הזה של Maps Live נסביר את העקרונות הבסיסיים להוספת סמנים למפה באמצעות ה-SDK של מפות Google ל-Android.

הוסף סמן

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

Kotlin

override fun onMapReady(googleMap: GoogleMap) {
    // Add a marker in Sydney, Australia,
    // and move the map's camera to the same location.
    val sydney = LatLng(-33.852, 151.211)
    googleMap.addMarker(
        MarkerOptions()
            .position(sydney)
            .title("Marker in Sydney")
    )
    googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}

      

Java

@Override
public void onMapReady(GoogleMap googleMap) {
    // Add a marker in Sydney, Australia,
    // and move the map's camera to the same location.
    LatLng sydney = new LatLng(-33.852, 151.211);
    googleMap.addMarker(new MarkerOptions()
        .position(sydney)
        .title("Marker in Sydney"));
    googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}

      

הצגת מידע נוסף על סמנים

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

שיוך נתונים לסמן

אפשר לאחסן אובייקט נתונים שרירותי עם סמן באמצעות Marker.setTag(), ולאחזר את אובייקט הנתונים באמצעות Marker.getTag(). הדוגמה הבאה מראה איך אפשר לספור את מספר הפעמים שבוצעו קליקים על סמן באמצעות תגים:

Kotlin

/**
 * A demo class that stores and retrieves data objects with each marker.
 */
class MarkerDemoActivity : AppCompatActivity(),
    OnMarkerClickListener, OnMapReadyCallback {
    private val PERTH = LatLng(-31.952854, 115.857342)
    private val SYDNEY = LatLng(-33.87365, 151.20689)
    private val BRISBANE = LatLng(-27.47093, 153.0235)

    private var markerPerth: Marker? = null
    private var markerSydney: Marker? = null
    private var markerBrisbane: Marker? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_markers)
        val mapFragment =
            supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
        mapFragment!!.getMapAsync(this)
    }

    /** Called when the map is ready.  */
    override fun onMapReady(map: GoogleMap) {
        // Add some markers to the map, and add a data object to each marker.
        markerPerth = map.addMarker(
            MarkerOptions()
                .position(PERTH)
                .title("Perth")
        )
        markerPerth?.tag = 0
        markerSydney = map.addMarker(
            MarkerOptions()
                .position(SYDNEY)
                .title("Sydney")
        )
        markerSydney?.tag = 0
        markerBrisbane = map.addMarker(
            MarkerOptions()
                .position(BRISBANE)
                .title("Brisbane")
        )
        markerBrisbane?.tag = 0

        // Set a listener for marker click.
        map.setOnMarkerClickListener(this)
    }

    /** Called when the user clicks a marker.  */
    override fun onMarkerClick(marker: Marker): Boolean {

        // Retrieve the data from the marker.
        val clickCount = marker.tag as? Int

        // Check if a click count was set, then display the click count.
        clickCount?.let {
            val newClickCount = it + 1
            marker.tag = newClickCount
            Toast.makeText(
                this,
                "${marker.title} has been clicked $newClickCount times.",
                Toast.LENGTH_SHORT
            ).show()
        }

        // Return false to indicate that we have not consumed the event and that we wish
        // for the default behavior to occur (which is for the camera to move such that the
        // marker is centered and for the marker's info window to open, if it has one).
        return false
    }
}

      

Java

/**
 * A demo class that stores and retrieves data objects with each marker.
 */
public class MarkerDemoActivity extends AppCompatActivity implements
    GoogleMap.OnMarkerClickListener,
    OnMapReadyCallback {

    private final LatLng PERTH = new LatLng(-31.952854, 115.857342);
    private final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
    private final LatLng BRISBANE = new LatLng(-27.47093, 153.0235);

    private Marker markerPerth;
    private Marker markerSydney;
    private Marker markerBrisbane;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_markers);
        SupportMapFragment mapFragment =
            (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    /** Called when the map is ready. */
    @Override
    public void onMapReady(GoogleMap map) {
        // Add some markers to the map, and add a data object to each marker.
        markerPerth = map.addMarker(new MarkerOptions()
            .position(PERTH)
            .title("Perth"));
        markerPerth.setTag(0);

        markerSydney = map.addMarker(new MarkerOptions()
            .position(SYDNEY)
            .title("Sydney"));
        markerSydney.setTag(0);

        markerBrisbane = map.addMarker(new MarkerOptions()
            .position(BRISBANE)
            .title("Brisbane"));
        markerBrisbane.setTag(0);

        // Set a listener for marker click.
        map.setOnMarkerClickListener(this);
    }

    /** Called when the user clicks a marker. */
    @Override
    public boolean onMarkerClick(final Marker marker) {

        // Retrieve the data from the marker.
        Integer clickCount = (Integer) marker.getTag();

        // Check if a click count was set, then display the click count.
        if (clickCount != null) {
            clickCount = clickCount + 1;
            marker.setTag(clickCount);
            Toast.makeText(this,
                marker.getTitle() +
                    " has been clicked " + clickCount + " times.",
                Toast.LENGTH_SHORT).show();
        }

        // Return false to indicate that we have not consumed the event and that we wish
        // for the default behavior to occur (which is for the camera to move such that the
        // marker is centered and for the marker's info window to open, if it has one).
        return false;
    }
}

      

ריכזנו כאן כמה דוגמאות לתרחישים שבהם כדאי לאחסן ולשלוף נתונים באמצעות סמנים:

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

איך הופכים סמן לניתן לגרירה

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

כברירת מחדל, אי אפשר לגרור סמנים. עליכם להגדיר את הסמן באופן מפורש כך שיהיה ניתן לגרירה באמצעות MarkerOptions.draggable(boolean) לפני ההוספה למפה, או Marker.setDraggable(boolean) אחרי הוספת הסמן למפה. אפשר להאזין לאירועי גרירה על הסמן, כפי שמתואר בקטע אירועי גרירה של סמן.

קטע הקוד הבא מוסיף סמן שניתן לגרירה בפריס, אוסטרליה.

Kotlin

val perthLocation = LatLng(-31.90, 115.86)
val perth = map.addMarker(
    MarkerOptions()
        .position(perthLocation)
        .draggable(true)
)

      

Java

final LatLng perthLocation = new LatLng(-31.90, 115.86);
Marker perth = map.addMarker(
    new MarkerOptions()
        .position(perthLocation)
        .draggable(true));

      

התאמה אישית של סמן

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

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

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

תפקיד (חובה)
הערך של LatLng למיקום הסמן במפה. זהו המאפיין היחיד שנדרש באובייקט Marker.
עוגן
הנקודה בתמונה שתוצג במיקום LatLng של הסמן. מיקום ברירת המחדל הוא באמצע התחתון של התמונה.
אלפא
הגדרת השקיפות של הסמן. ברירת המחדל היא 1.0.
כותרת
מחרוזת שמוצגת בחלון המידע כשהמשתמש מקיש על הסמן.
קטע טקסט
טקסט נוסף שמוצג מתחת לכותרת.
סמל
מפת סיביות (bitmap) שמוצגת במקום תמונת הסמן שמוגדרת כברירת מחדל.
אפשר לגרירה
מגדירים לערך true אם רוצים לאפשר למשתמש להזיז את הסמן. ברירת המחדל היא false.
גלוי
מגדירים את הערך כ-false כדי שהסמן יהיה בלתי נראה. ברירת המחדל היא true.
כיוון אופקי או כיוון של מודעה על לוח
ברירת המחדל של הסמנים היא כיוון של מודעות חוצות, כלומר הם מוצגים בכיוון של מסך המכשיר ולא בכיוון של המפה. סיבוב המפה, הטייה או שינוי מרחק התצוגה של המפה לא משנים את כיוון הסמן. אפשר להגדיר את הכיוון של הסמן כך שיהיה שטוח על פני כדור הארץ. סמנים שטוחים מסתובבים כשמסובבים את המפה, ושינוי נקודת המבט שלהם מתרחש כשמשנים את נטיית המפה. בדומה לסמנים בלוח המודעות, סמנים שטוחים שומרים על גודלם כשמגדילים או מקטינים את המפה.
סיבוב
הכיוון של הסמן, שמצוין במעלות בכיוון השעון. המיקום שמוגדר כברירת מחדל משתנה אם הסמן שטוח. המיקום שמוגדר כברירת מחדל לסמן שטוח הוא בכיוון צפון. כשהסימן לא שטוח, מיקום ברירת המחדל שלו הוא כלפי מעלה והכיוון שלו הוא תמיד לכיוון המצלמה.

קטע הקוד הבא יוצר סמן פשוט עם סמל ברירת המחדל.

Kotlin

val melbourneLocation = LatLng(-37.813, 144.962)
val melbourne = map.addMarker(
    MarkerOptions()
        .position(melbourneLocation)
)

      

Java

final LatLng melbourneLocation = new LatLng(-37.813, 144.962);
Marker melbourne = map.addMarker(
    new MarkerOptions()
        .position(melbourneLocation));

      

התאמה אישית של צבע הסמן

אפשר להתאים אישית את הצבע של תמונת הסמן שמוגדרת כברירת מחדל על ידי העברת אובייקט BitmapDescriptor ל-method icon(). אפשר להשתמש בקבוצה של צבעים מוגדרים מראש באובייקט BitmapDescriptorFactory, או להגדיר צבע סימון בהתאמה אישית באמצעות השיטה BitmapDescriptorFactory.defaultMarker(float hue). הטון הוא ערך בין 0 ל-360 שמייצג נקודות בגלגל הצבעים.

Kotlin

val melbourneLocation = LatLng(-37.813, 144.962)
val melbourne = map.addMarker(
    MarkerOptions()
        .position(melbourneLocation)
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
)

      

Java

final LatLng melbourneLocation = new LatLng(-37.813, 144.962);
Marker melbourne = map.addMarker(
    new MarkerOptions()
        .position(melbourneLocation)
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));

      

התאמה אישית של שקיפות הסמן

אפשר לשלוט בשקיפות של סמן באמצעות השיטה MarkerOptions.alpha(). צריך לציין את הערך של אלפא כערך של משתנה מסוג float בין 0.0 ל-1.0, כאשר 0 הוא שקוף לחלוטין ו-1 הוא אטום לחלוטין.

Kotlin

val melbourneLocation = LatLng(-37.813, 144.962)
val melbourne = map.addMarker(
    MarkerOptions()
        .position(melbourneLocation)
        .alpha(0.7f)
)

      

Java

final LatLng melbourneLocation = new LatLng(-37.813, 144.962);
Marker melbourne = map.addMarker(new MarkerOptions()
    .position(melbourneLocation)
    .alpha(0.7f));

      

התאמה אישית של תמונת הסמן

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

fromAsset(String assetName)
יצירת סמן בהתאמה אישית באמצעות השם של קובץ Bitmap בספריית הנכסים.
fromBitmap(Bitmap image)
יצירת סמן מותאם אישית מתמונה של מפת סיביות (Bitmap).
fromFile(String fileName)
יצירת סמל בהתאמה אישית באמצעות השם של קובץ תמונה בפורמט Bitmap שנמצא באחסון הפנימי.
fromPath(String absolutePath)
יצירת סמן בהתאמה אישית מנתיב קובץ מוחלט של קובץ Bitmap.
fromResource(int resourceId)
יצירת סמן מותאם אישית באמצעות מזהה המשאב של קובץ Bitmap.

קטע הקוד הבא יוצר סמן עם סמל מותאם אישית.

Kotlin

val melbourneLocation = LatLng(-37.813, 144.962)
val melbourne = map.addMarker(
    MarkerOptions()
        .position(melbourneLocation)
        .title("Melbourne")
        .snippet("Population: 4,137,400")
        .icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow))
)

      

Java

final LatLng melbourneLocation = new LatLng(-37.813, 144.962);
Marker melbourne = map.addMarker(
    new MarkerOptions()
        .position(melbourneLocation)
        .title("Melbourne")
        .snippet("Population: 4,137,400")
        .icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow)));

      

יישור סמן

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

כדי לשנות את הכיוון של הסמן, מגדירים את המאפיין flat של הסמן לערך true.

Kotlin

val perthLocation = LatLng(-31.90, 115.86)
val perth = map.addMarker(
    MarkerOptions()
        .position(perthLocation)
        .flat(true)
)

      

Java

final LatLng perthLocation = new LatLng(-31.90, 115.86);
Marker perth = map.addMarker(
    new MarkerOptions()
        .position(perthLocation)
        .flat(true));

      

סיבוב של סמן

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

בדוגמה הבאה הסמן מסתובב ב-90°. הגדרת נקודת העוגן כ-0.5,0.5 גורמת לסמן להסתובב סביב מרכזו, במקום סביב הבסיס שלו.

Kotlin

val perthLocation = LatLng(-31.90, 115.86)
val perth = map.addMarker(
    MarkerOptions()
        .position(perthLocation)
        .anchor(0.5f, 0.5f)
        .rotation(90.0f)
)

      

Java

final LatLng perthLocation = new LatLng(-31.90, 115.86);
Marker perth = map.addMarker(
    new MarkerOptions()
        .position(perthLocation)
        .anchor(0.5f,0.5f)
        .rotation(90.0f));

      

אינדקס Z של סמן

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

מגדירים את הערך של z-index באובייקט האפשרויות של הסמן באמצעות קריאה ל-MarkerOptions.zIndex(), כפי שמתואר בקטע הקוד הבא:

Kotlin

map.addMarker(
    MarkerOptions()
        .position(LatLng(10.0, 10.0))
        .title("Marker z1")
        .zIndex(1.0f)
)

      

Java

map.addMarker(new MarkerOptions()
    .position(new LatLng(10, 10))
    .title("Marker z1")
    .zIndex(1.0f));

      

אפשר לגשת למאפיין z-index של הסמן באמצעות קריאה ל-Marker.getZIndex(), ולשנות אותו באמצעות קריאה ל-Marker.setZIndex().

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

בהמשך מוסבר על ההשפעה של z-index על אירועי קליקים.

טיפול באירועי סמן

באמצעות Maps API אפשר להאזין לאירועים של סמנים ולענות להם. כדי להאזין לאירועים האלה, צריך להגדיר את ה-listener המתאים באובייקט GoogleMap שאליו שייכים הסמנים. כשהאירוע מתרחש באחד מהסמנים במפה, הקריאה החוזרת של המאזינים תופעל עם האובייקט Marker התואם שמועבר כפרמטר. כדי להשוות את אובייקט ה-Marker הזה להפניה שלכם לאובייקט Marker, צריך להשתמש ב-equals() ולא ב-==.

אתם יכולים להאזין לאירועים הבאים:

אירועי קליקים על סמנים

ניתן להשתמש ב-OnMarkerClickListener כדי להאזין לאירועי קליק בסמן. כדי להגדיר את המאזין הזה במפה, צריך להפעיל את הפונקציה GoogleMap.setOnMarkerClickListener(OnMarkerClickListener). כשמשתמש לוחץ על סמן, תתבצע קריאה ל-onMarkerClick(Marker) והסמן יועבר כארגומנטים. השיטה הזו מחזירה ערך בוליאני שמציין אם השתמשתם באירוע (כלומר, אם אתם רוצים לבטל את התנהגות ברירת המחדל). אם הפונקציה מחזירה את הערך false, ההתנהגות שמוגדרת כברירת מחדל תתבצע בנוסף להתנהגות בהתאמה אישית. התנהגות ברירת המחדל של אירוע מסוג לחיצה על סמן היא להציג את חלון המידע שלו (אם הוא זמין) ולהזיז את המצלמה כך שהסמן יתרכז במפה.

השפעת הערך של z-index על אירועי קליקים:

  • כשמשתמש לוחץ על אשכול של סמנים, אירוע הקליק מופעל עבור הסמן עם הערך הגבוה ביותר של z-index.
  • לכל היותר אירוע אחד מופעל לכל קליק. במילים אחרות, הקליק לא מועבר לסימנים או לשכבות-על אחרות עם ערכי z-index נמוכים יותר.
  • לחיצה על אשכול של סמנים גורמת לכך שלחיצות נוספות יעברו באשכול ויבחרו כל אחד מהסמנים בתורו. הסדר של המחזור נותן עדיפות קודם ל-z-index ואז לקרבה לנקודת הקליק.
  • אם המשתמש לוחץ מחוץ לקרבה לאשכול, ה-API יחשב מחדש את האשכול ומאפס את המצב של מחזור הקליקים כך שהוא יתחיל מההתחלה.
  • אירוע הקליק עובר דרך אשכולות של סמנים לצורות ולשכבות-על אחרות, לפני שהוא מתחיל מחדש את המחזור.
  • בפועל, סמנים נחשבים לקבוצה נפרדת של אינדקס z, בהשוואה לשכבות-על או לצורות אחרות (קווים פוליגוניים, פוליגונים, מעגלים ו/או שכבות-על של פני השטח), בלי קשר ל-z-index של שאר שכבות-העל. אם מספר סמנים, שכבות-על או צורות מופיעות זה על זה בשכבת-על, האירוע מסוג קליק עובר קודם בין אשכול הסמנים ואז מופעל לצורות או שכבות-על אחרות שאפשר ללחוץ עליהן, על סמך ערכי ה-z-index.

אירועי גרירה של סמנים

ניתן להשתמש ב-OnMarkerDragListener כדי להאזין לאירועי גרירה על סמן. כדי להגדיר את המאזין הזה במפה, צריך להפעיל את הפונקציה GoogleMap.setOnMarkerDragListener. כדי לגרור סמן, המשתמש צריך ללחוץ לחיצה ארוכה על הסמן. כשהמשתמש יסיר את האצבע מהמסך, הסמן יישאר במיקום הזה. כשגוררים סמן, מתבצעת הפעלה ראשונית של onMarkerDragStart(Marker). בזמן גרירת הסמן, מתבצעת קריאה שוטפת ל-onMarkerDrag(Marker). בסוף ההזזה, מתבצעת קריאה ל-onMarkerDragEnd(Marker). אפשר לקבל את המיקום של הסמן בכל שלב באמצעות קריאה ל-Marker.getPosition().