סמנים

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

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

דוגמאות קוד

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

קוטלין

Java

מבוא

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

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

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

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

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

הוספת סמן

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

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 של סמן מסוים.

הגדרת סמן כניתן לגרירה

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

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

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

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() של סמל. אפשר להשתמש בקבוצה של צבעים מוגדרים מראש באובייקט 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)));

      

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

ניתן לשלוט באטימות של סמן באמצעות השיטה MarkOptions.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-index

ה-z-index מציין את סדר הערימה של הסמן הזה, ביחס לסמנים אחרים במפה. סמן עם מדד z גבוה משורטט מעל סמנים עם מדדי Z נמוכים יותר. ערך ברירת המחדל של 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-index של שאר שכבות-העל. סמנים נחשבים בפועל לקבוצה נפרדת של z-index בהשוואה לשכבות-על אחרות.

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

טיפול באירועי סמנים

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

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

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

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

ההשפעה של אינדקס z על אירועים מסוג קליק:

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

אירועי גרירת סמן

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