סמנים מציינים מיקומים במפה. סמן ברירת המחדל משתמש בסמל רגיל, שמשותף למראה ולתחושה של מפות Google. אפשר לשנות את הצבע, התמונה או נקודת העיגון של הסמל באמצעות ה-API. סמנים הם אובייקטים מהסוג Marker
, והם מתווספים למפה באמצעות השיטה GoogleMap.addMarker(markerOptions)
.
הסמנים נועדו להיות אינטראקטיביים. הם מקבלים אירועים של click
כברירת מחדל, ולרוב משתמשים בהם עם פונקציות event listener כדי להציג חלונות מידע. הגדרה של המאפיין draggable
של סמן לערך true
מאפשרת למשתמש לשנות את מיקום הסמן. לחיצה ארוכה מפעילה את האפשרות להזיז את הסמן.
כברירת מחדל, כשמשתמש מקיש על סמן, סרגל הכלים של המפה מופיע בחלק התחתון השמאלי של המפה, ומאפשר למשתמש גישה מהירה לאפליקציה לנייד של מפות Google. אפשר להשבית את סרגל הכלים. מידע נוסף זמין במדריך לאמצעי בקרה.
הוסף סמן
בדוגמה הבאה מוסבר איך להוסיף סמן למפה. הסמן נוצר בקואורדינטות -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.
- כותרת
- מחרוזת שמוצגת בחלון המידע כשהמשתמש מקיש על הסמן.
- קטע טקסט
- טקסט נוסף שמוצג מתחת לכותרת.
- סמל
- מפת סיביות שמוצגת במקום תמונת הסמן שמוגדרת כברירת מחדל.
- ניתן לגרירה
- מגדירים את הערך
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
לשיטה 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(). ערך האלפא צריך להיות מספר עשרוני בין 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)
- Creates a custom marker using the resource ID of a Bitmap image.
קטע הקוד שבהמשך יוצר סמן עם סמל מותאם אישית.
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()
method. הסיבוב נמדד במעלות בכיוון השעון מהמיקום שמוגדר כברירת מחדל. כשהסמן שטוח על המפה, מיקום ברירת המחדל הוא צפון. אם הסמן לא שטוח, מיקום ברירת המחדל הוא כלפי מעלה, והסיבוב הוא כזה שהסמן תמיד פונה למצלמה.
בדוגמה שלמטה, הסמן מסובב ב-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));
Marker z-index
המאפיין z-index מציין את סדר הערימה של הסמן הזה, ביחס לסמנים אחרים במפה. סמן עם ערך גבוה של 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-index נפרדת בהשוואה לשכבות-על אחרות.
בהמשך המאמר מוסבר על ההשפעה של z-index על אירועי קליקים.
טיפול באירועים של סמנים
Maps API מאפשר לכם להאזין לאירועים של סמנים ולהגיב להם. כדי להפעיל listener לאירועים האלה, צריך להגדיר את ה-listener המתאים באובייקט GoogleMap
שאליו שייכים הסמנים. כשהאירוע מתרחש באחד הסמנים במפה, מתבצעת קריאה חוזרת (callback) של מאזין עם אובייקט Marker
מתאים שמועבר כפרמטר. כדי להשוות את אובייקט Marker
הזה להפניה שלכם לאובייקט Marker
, צריך להשתמש ב-equals()
ולא ב-==
.
אפשר להאזין לאירועים הבאים:
אירועים מסוג קליק על סמן
אפשר להשתמש ב-OnMarkerClickListener
כדי להאזין לאירועי קליקים על הסמן. כדי להגדיר את מאזין המיקום במפה, קוראים ל-GoogleMap.setOnMarkerClickListener(OnMarkerClickListener)
. כשמשתמש לוחץ על סמן, הפונקציה onMarkerClick(Marker)
מופעלת והסמן מועבר כארגומנט. השיטה הזו מחזירה ערך בוליאני שמציין אם האירוע נצרך (כלומר, אם רוצים לבטל את התנהגות ברירת המחדל). אם הערך שמוחזר הוא false
, ההתנהגות שמוגדרת כברירת מחדל תתרחש בנוסף להתנהגות המותאמת אישית. ההתנהגות שמוגדרת כברירת מחדל כשלוחצים על סמן היא הצגת חלון המידע שלו (אם הוא זמין) והזזת המצלמה כך שהסמן יהיה במרכז המפה.
ההשפעה של z-index על אירועי קליקים:
- כשמשתמש לוחץ על קבוצת סמנים, אירוע הלחיצה מופעל עבור הסמן עם ערך ה-z-index הגבוה ביותר.
- לכל היותר מופעל אירוע אחד לכל קליק. במילים אחרות, הקליק לא מועבר לסמנים או לשכבות-על אחרות עם ערכי z-index נמוכים יותר.
- לחיצה על אשכול של סמנים גורמת ללחיצות הבאות לעבור בין הסמנים באשכול, ולבחור כל אחד מהם בתורו. בסדר הפעולות של המחזור, קודם ניתן עדיפות ל-z-index, ואז לקרבה לנקודת הלחיצה.
- אם המשתמש לוחץ מחוץ לקרבה של האשכול, ה-API מבצע מחדש את החישוב של האשכול ומאפס את מצב מחזור הקליקים, כך שהוא מתחיל מההתחלה.
- סמנים נחשבים למעשה לקבוצת z-index נפרדת בהשוואה לשכבות-על או לצורות אחרות (קווי פולי, מצולעים, עיגולים ו/או שכבות-על של קרקע), ללא קשר ל-z-index של שכבות-העל האחרות. אם כמה סמנים, שכבות-על או צורות מוצבים אחד על השני, אירוע הקליק עובר קודם בין הסמנים באוסף.
אירועי גרירה של סמנים
אפשר להשתמש ב-OnMarkerDragListener
כדי להאזין לאירועי גרירה של סמן. כדי להגדיר את מאזין המיקום במפה, קוראים ל-GoogleMap.setOnMarkerDragListener
. כדי לגרור סמן, המשתמש צריך ללחוץ לחיצה ארוכה על הסמן. כשהמשתמש מסיר את האצבע מהמסך, הסמן נשאר במיקום הזה. כשגוררים סמן, מתבצעת קריאה ראשונית ל-onMarkerDragStart(Marker)
. בזמן גרירת הסמן, מתבצעת קריאה ל-onMarkerDrag(Marker)
באופן קבוע. בסוף הגרירה, מתבצעת קריאה ל-onMarkerDragEnd(Marker)
. אפשר לקבל את המיקום של הסמן בכל שלב באמצעות קריאה ל-Marker.getPosition()
.