Implementation of Map Marker Display in a Mobile Application
Adding a single marker is three lines of code. Displaying 500 markers with custom icons, correct reuse, and taps without freezing — that's a task with nuances.
Custom Icons: Bitmap vs Vector
Google Maps Android SDK accepts BitmapDescriptor, MapKit — ImageProvider, Mapbox — Drawable / UIImage. Rendering a bitmap from Canvas must be done once and cached — not in onMapReady for each marker separately.
// Google Maps Android — cached BitmapDescriptor
private val markerCache = HashMap<String, BitmapDescriptor>()
fun getMarkerIcon(type: String): BitmapDescriptor {
return markerCache.getOrPut(type) {
val bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = when (type) {
"cafe" -> Color.parseColor("#E74C3C")
"shop" -> Color.parseColor("#3498DB")
else -> Color.GRAY
}
}
canvas.drawCircle(24f, 24f, 20f, paint)
BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
Creating Bitmap each time you add a marker is a direct path to OutOfMemoryError with 200+ objects on screen.
Callout / InfoWindow on Tap
In Google Maps on Android, InfoWindow renders as a static screenshot — buttons don't work inside and dynamic content doesn't update. For an interactive popup, use ViewAnnotation (Maps SDK v3+) or your own FrameLayout on top of the map with positioning via Projection.toScreenLocation.
// iOS MapKit — custom callout via UIView
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "custom")
view.image = UIImage(named: "pin")
view.canShowCallout = false // disable standard
// Add custom callout in didSelect
return view
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let callout = CustomCalloutView(annotation: view.annotation)
callout.center = CGPoint(x: view.bounds.midX, y: -callout.bounds.height / 2)
view.addSubview(callout)
}
Performance: 500+ Markers
With a large number of objects, native marker APIs start to slow down — each marker is a separate view. The threshold depends on the device: on budget Android, noticeable freezes appear after 150-200 Marker objects when adding simultaneously.
Solution — GeoJSON layer in Mapbox or TileOverlay in Google Maps: points render as part of the map style, without creating objects for each coordinate.
For scenarios where you still need native markers with taps — add them in chunks via Handler.postDelayed or coroutines:
lifecycleScope.launch {
locations.chunked(50).forEach { chunk ->
chunk.forEach { loc ->
googleMap.addMarker(
MarkerOptions()
.position(LatLng(loc.lat, loc.lng))
.icon(getMarkerIcon(loc.type))
)
}
delay(16) // one frame, let UI breathe
}
}
Timeline
4 hours — 2 days. One marker type with callout — half a day. Several types with icon cache and interactive popups — 1–2 days. Cost is calculated individually.







