Skip to content

Commit 66fb740

Browse files
committed
feat: add custom info window support
1 parent 3c274a1 commit 66fb740

File tree

14 files changed

+555
-104
lines changed

14 files changed

+555
-104
lines changed

android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package com.rngooglemapsplus
22

3+
import CircleTag
4+
import MarkerTag
5+
import PolygonTag
6+
import PolylineTag
37
import android.annotation.SuppressLint
48
import android.graphics.Bitmap
59
import android.location.Location
610
import android.util.Base64
711
import android.util.Size
12+
import android.view.View
813
import android.widget.FrameLayout
914
import androidx.core.graphics.scale
1015
import com.facebook.react.bridge.LifecycleEventListener
@@ -45,6 +50,8 @@ import com.rngooglemapsplus.extensions.toRnCamera
4550
import com.rngooglemapsplus.extensions.toRnLatLng
4651
import com.rngooglemapsplus.extensions.toRnLocation
4752
import com.rngooglemapsplus.extensions.toRnRegion
53+
import idTag
54+
import tagData
4855
import java.io.ByteArrayInputStream
4956
import java.io.ByteArrayOutputStream
5057
import java.io.File
@@ -74,14 +81,15 @@ class GoogleMapsViewImpl(
7481
GoogleMap.OnInfoWindowLongClickListener,
7582
GoogleMap.OnMyLocationClickListener,
7683
GoogleMap.OnMyLocationButtonClickListener,
84+
GoogleMap.InfoWindowAdapter,
7785
LifecycleEventListener {
7886
private var initialized = false
7987
private var loaded = false
8088
private var destroyed = false
8189
private var googleMap: GoogleMap? = null
8290
private var mapView: MapView? = null
8391

84-
private val pendingMarkers = mutableListOf<Pair<String, MarkerOptions>>()
92+
private val pendingMarkers = mutableListOf<Triple<String, MarkerOptions, MarkerTag>>()
8593
private val pendingPolylines = mutableListOf<Pair<String, PolylineOptions>>()
8694
private val pendingPolygons = mutableListOf<Pair<String, PolygonOptions>>()
8795
private val pendingCircles = mutableListOf<Pair<String, CircleOptions>>()
@@ -142,6 +150,7 @@ class GoogleMapsViewImpl(
142150
googleMap?.setOnInfoWindowLongClickListener(this@GoogleMapsViewImpl)
143151
googleMap?.setOnMyLocationClickListener(this@GoogleMapsViewImpl)
144152
googleMap?.setOnMyLocationButtonClickListener(this@GoogleMapsViewImpl)
153+
googleMap?.setInfoWindowAdapter(this@GoogleMapsViewImpl)
145154
loaded = true
146155
onMapLoaded?.invoke(
147156
map.projection.visibleRegion.toRnRegion(),
@@ -217,7 +226,7 @@ class GoogleMapsViewImpl(
217226
locationConfig = locationConfig
218227

219228
if (pendingMarkers.isNotEmpty()) {
220-
pendingMarkers.forEach { (id, opts) -> internalAddMarker(id, opts) }
229+
pendingMarkers.forEach { (id, opts, markerTag) -> internalAddMarker(id, opts, markerTag) }
221230
pendingMarkers.clear()
222231
}
223232
if (pendingPolylines.isNotEmpty()) {
@@ -368,24 +377,36 @@ class GoogleMapsViewImpl(
368377
var onMapPress: ((RNLatLng) -> Unit)? = null
369378
var onMapLongPress: ((RNLatLng) -> Unit)? = null
370379
var onPoiPress: ((String, String, RNLatLng) -> Unit)? = null
371-
var onMarkerPress: ((String?) -> Unit)? = null
372-
var onPolylinePress: ((String?) -> Unit)? = null
373-
var onPolygonPress: ((String?) -> Unit)? = null
374-
var onCirclePress: ((String?) -> Unit)? = null
375-
var onMarkerDragStart: ((String?, RNLatLng) -> Unit)? = null
376-
var onMarkerDrag: ((String?, RNLatLng) -> Unit)? = null
377-
var onMarkerDragEnd: ((String?, RNLatLng) -> Unit)? = null
380+
var onMarkerPress: ((String) -> Unit)? = null
381+
var onPolylinePress: ((String) -> Unit)? = null
382+
var onPolygonPress: ((String) -> Unit)? = null
383+
var onCirclePress: ((String) -> Unit)? = null
384+
var onMarkerDragStart: ((String, RNLatLng) -> Unit)? = null
385+
var onMarkerDrag: ((String, RNLatLng) -> Unit)? = null
386+
var onMarkerDragEnd: ((String, RNLatLng) -> Unit)? = null
378387
var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Unit)? = null
379388
var onIndoorLevelActivated: ((RNIndoorLevel) -> Unit)? = null
380-
var onInfoWindowPress: ((String?) -> Unit)? = null
381-
var onInfoWindowClose: ((String?) -> Unit)? = null
382-
var onInfoWindowLongPress: ((String?) -> Unit)? = null
389+
var onInfoWindowPress: ((String) -> Unit)? = null
390+
var onInfoWindowClose: ((String) -> Unit)? = null
391+
var onInfoWindowLongPress: ((String) -> Unit)? = null
383392
var onMyLocationPress: ((RNLocation) -> Unit)? = null
384393
var onMyLocationButtonPress: ((Boolean) -> Unit)? = null
385394
var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
386395
var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
387396
var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
388397

398+
fun showMarkerInfoWindow(id: String) =
399+
onUi {
400+
val marker = markersById[id] ?: return@onUi
401+
marker.showInfoWindow()
402+
}
403+
404+
fun hideMarkerInfoWindow(id: String) =
405+
onUi {
406+
val marker = markersById[id] ?: return@onUi
407+
marker.hideInfoWindow()
408+
}
409+
389410
fun setCamera(
390411
cameraPosition: CameraPosition,
391412
animated: Boolean,
@@ -523,21 +544,30 @@ class GoogleMapsViewImpl(
523544
fun addMarker(
524545
id: String,
525546
opts: MarkerOptions,
547+
markerTag: MarkerTag,
526548
) = onUi {
527549
if (googleMap == null) {
528-
pendingMarkers.add(id to opts)
550+
pendingMarkers.add(Triple(id, opts, markerTag))
529551
return@onUi
530552
}
553+
531554
markersById.remove(id)?.remove()
532-
internalAddMarker(id, opts)
555+
internalAddMarker(id, opts, markerTag)
533556
}
534557

535558
private fun internalAddMarker(
536559
id: String,
537560
opts: MarkerOptions,
561+
markerTag: MarkerTag,
538562
) = onUi {
539-
val marker = googleMap?.addMarker(opts).also { it?.tag = id }
540-
if (marker != null) markersById[id] = marker
563+
val marker =
564+
googleMap?.addMarker(opts)?.apply {
565+
tag = markerTag
566+
}
567+
568+
if (marker != null) {
569+
markersById[id] = marker
570+
}
541571
}
542572

543573
fun updateMarker(
@@ -546,6 +576,10 @@ class GoogleMapsViewImpl(
546576
) = onUi {
547577
val marker = markersById[id] ?: return@onUi
548578
block(marker)
579+
if (marker.isInfoWindowShown) {
580+
marker.hideInfoWindow()
581+
marker.showInfoWindow()
582+
}
549583
}
550584

551585
fun removeMarker(id: String) =
@@ -576,7 +610,10 @@ class GoogleMapsViewImpl(
576610
id: String,
577611
opts: PolylineOptions,
578612
) = onUi {
579-
val pl = googleMap?.addPolyline(opts).also { it?.tag = id }
613+
val pl =
614+
googleMap?.addPolyline(opts).also {
615+
it?.tag = PolylineTag(id = id)
616+
}
580617
if (pl != null) polylinesById[id] = pl
581618
}
582619

@@ -616,7 +653,10 @@ class GoogleMapsViewImpl(
616653
id: String,
617654
opts: PolygonOptions,
618655
) = onUi {
619-
val polygon = googleMap?.addPolygon(opts).also { it?.tag = id }
656+
val polygon =
657+
googleMap?.addPolygon(opts).also {
658+
it?.tag = PolygonTag(id = id)
659+
}
620660
if (polygon != null) polygonsById[id] = polygon
621661
}
622662

@@ -656,7 +696,10 @@ class GoogleMapsViewImpl(
656696
id: String,
657697
opts: CircleOptions,
658698
) = onUi {
659-
val circle = googleMap?.addCircle(opts).also { it?.tag = id }
699+
val circle =
700+
googleMap?.addCircle(opts).also {
701+
it?.tag = CircleTag(id = id)
702+
}
660703
if (circle != null) circlesById[id] = circle
661704
}
662705

@@ -818,6 +861,7 @@ class GoogleMapsViewImpl(
818861
setOnInfoWindowLongClickListener(null)
819862
setOnMyLocationClickListener(null)
820863
setOnMyLocationButtonClickListener(null)
864+
setInfoWindowAdapter(null)
821865
}
822866
googleMap = null
823867
mapView?.apply {
@@ -873,25 +917,24 @@ class GoogleMapsViewImpl(
873917

874918
override fun onMarkerClick(marker: Marker): Boolean {
875919
onUi {
876-
marker.showInfoWindow()
877-
onMarkerPress?.invoke(marker.tag?.toString())
920+
onMarkerPress?.invoke(marker.idTag)
878921
}
879922
return uiSettings?.consumeOnMarkerPress ?: false
880923
}
881924

882925
override fun onPolylineClick(polyline: Polyline) =
883926
onUi {
884-
onPolylinePress?.invoke(polyline.tag?.toString())
927+
onPolylinePress?.invoke(polyline.idTag)
885928
}
886929

887930
override fun onPolygonClick(polygon: Polygon) =
888931
onUi {
889-
onPolygonPress?.invoke(polygon.tag?.toString())
932+
onPolygonPress?.invoke(polygon.idTag)
890933
}
891934

892935
override fun onCircleClick(circle: Circle) =
893936
onUi {
894-
onCirclePress?.invoke(circle.tag?.toString())
937+
onCirclePress?.invoke(circle.idTag)
895938
}
896939

897940
override fun onMapClick(coordinates: LatLng) =
@@ -906,17 +949,17 @@ class GoogleMapsViewImpl(
906949

907950
override fun onMarkerDragStart(marker: Marker) =
908951
onUi {
909-
onMarkerDragStart?.invoke(marker.tag?.toString(), marker.position.toRnLatLng())
952+
onMarkerDragStart?.invoke(marker.idTag, marker.position.toRnLatLng())
910953
}
911954

912955
override fun onMarkerDrag(marker: Marker) =
913956
onUi {
914-
onMarkerDrag?.invoke(marker.tag?.toString(), marker.position.toRnLatLng())
957+
onMarkerDrag?.invoke(marker.idTag, marker.position.toRnLatLng())
915958
}
916959

917960
override fun onMarkerDragEnd(marker: Marker) =
918961
onUi {
919-
onMarkerDragEnd?.invoke(marker.tag?.toString(), marker.position.toRnLatLng())
962+
onMarkerDragEnd?.invoke(marker.idTag, marker.position.toRnLatLng())
920963
}
921964

922965
override fun onIndoorBuildingFocused() =
@@ -941,17 +984,17 @@ class GoogleMapsViewImpl(
941984

942985
override fun onInfoWindowClick(marker: Marker) =
943986
onUi {
944-
onInfoWindowPress?.invoke(marker.tag?.toString())
987+
onInfoWindowPress?.invoke(marker.idTag)
945988
}
946989

947990
override fun onInfoWindowClose(marker: Marker) =
948991
onUi {
949-
onInfoWindowClose?.invoke(marker.tag?.toString())
992+
onInfoWindowClose?.invoke(marker.idTag)
950993
}
951994

952995
override fun onInfoWindowLongClick(marker: Marker) =
953996
onUi {
954-
onInfoWindowLongPress?.invoke(marker.tag?.toString())
997+
onInfoWindowLongPress?.invoke(marker.idTag)
955998
}
956999

9571000
override fun onMyLocationClick(location: Location) =
@@ -965,4 +1008,8 @@ class GoogleMapsViewImpl(
9651008
}
9661009
return uiSettings?.consumeOnMyLocationButtonPress ?: false
9671010
}
1011+
1012+
override fun getInfoContents(marker: Marker): View? = null
1013+
1014+
override fun getInfoWindow(marker: Marker): View? = markerBuilder.buildInfoWindow(marker.tagData.iconSvg)
9681015
}

android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.rngooglemapsplus
22

3+
import MarkerTag
34
import android.graphics.Bitmap
45
import android.graphics.BitmapFactory
56
import android.graphics.Canvas
67
import android.graphics.Typeface
8+
import android.graphics.drawable.PictureDrawable
79
import android.util.Base64
810
import android.util.LruCache
11+
import android.widget.ImageView
12+
import android.widget.LinearLayout
913
import androidx.core.graphics.createBitmap
1014
import com.caverock.androidsvg.SVG
1115
import com.caverock.androidsvg.SVGExternalFileResolver
@@ -239,6 +243,10 @@ class MapMarkerBuilder(
239243
if (prev.zIndex != next.zIndex) {
240244
marker.zIndex = next.zIndex?.toFloat() ?: 0f
241245
}
246+
247+
if (prev.infoWindowIconSvg != next.infoWindowIconSvg) {
248+
marker.tag = MarkerTag(id = next.id, iconSvg = next.infoWindowIconSvg)
249+
}
242250
}
243251

244252
fun buildIconAsync(
@@ -296,6 +304,31 @@ class MapMarkerBuilder(
296304
iconCache.evictAll()
297305
}
298306

307+
fun buildInfoWindow(iconSvg: RNMarkerSvg?): ImageView? {
308+
val iconSvg = iconSvg ?: return null
309+
310+
val svgView =
311+
ImageView(context).apply {
312+
layoutParams =
313+
LinearLayout.LayoutParams(
314+
iconSvg.width.dpToPx().toInt(),
315+
iconSvg.height.dpToPx().toInt(),
316+
)
317+
}
318+
319+
try {
320+
val svg = SVG.getFromString(iconSvg.svgString)
321+
svg.setDocumentWidth(iconSvg.width.dpToPx())
322+
svg.setDocumentHeight(iconSvg.height.dpToPx())
323+
val drawable = PictureDrawable(svg.renderToPicture())
324+
svgView.setImageDrawable(drawable)
325+
} catch (e: Exception) {
326+
return null
327+
}
328+
329+
return svgView
330+
}
331+
299332
private suspend fun renderBitmap(m: RNMarker): Bitmap? {
300333
m.iconSvg ?: return null
301334

0 commit comments

Comments
 (0)