Skip to content

Commit 8e32d14

Browse files
committed
feat: add mapCircle support
feat: add polyline, polygon and circle onPress support
1 parent 8fccfa9 commit 8e32d14

File tree

15 files changed

+437
-70
lines changed

15 files changed

+437
-70
lines changed

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

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import com.google.android.gms.maps.GoogleMap
1212
import com.google.android.gms.maps.GoogleMapOptions
1313
import com.google.android.gms.maps.MapView
1414
import com.google.android.gms.maps.model.CameraPosition
15+
import com.google.android.gms.maps.model.Circle
16+
import com.google.android.gms.maps.model.CircleOptions
1517
import com.google.android.gms.maps.model.LatLng
1618
import com.google.android.gms.maps.model.LatLngBounds
1719
import com.google.android.gms.maps.model.MapColorScheme
@@ -34,20 +36,26 @@ class GoogleMapsViewImpl(
3436
GoogleMap.OnCameraIdleListener,
3537
GoogleMap.OnMapClickListener,
3638
GoogleMap.OnMarkerClickListener,
39+
GoogleMap.OnPolylineClickListener,
40+
GoogleMap.OnPolygonClickListener,
41+
GoogleMap.OnCircleClickListener,
3742
LifecycleEventListener {
3843
private var initialized = false
3944
private var mapReady = false
4045
private var googleMap: GoogleMap? = null
4146
private var mapView: MapView? = null
42-
private val pendingPolygons = mutableListOf<Pair<String, PolygonOptions>>()
43-
private val pendingPolylines = mutableListOf<Pair<String, PolylineOptions>>()
47+
4448
private val pendingMarkers = mutableListOf<Pair<String, MarkerOptions>>()
45-
private var cameraMoveReason = -1
49+
private val pendingPolylines = mutableListOf<Pair<String, PolylineOptions>>()
50+
private val pendingPolygons = mutableListOf<Pair<String, PolygonOptions>>()
51+
private val pendingCircles = mutableListOf<Pair<String, CircleOptions>>()
4652

47-
private val polygonsById = mutableMapOf<String, Polygon>()
48-
private val polylinesById = mutableMapOf<String, Polyline>()
4953
private val markersById = mutableMapOf<String, Marker>()
54+
private val polylinesById = mutableMapOf<String, Polyline>()
55+
private val polygonsById = mutableMapOf<String, Polygon>()
56+
private val circlesById = mutableMapOf<String, Circle>()
5057

58+
private var cameraMoveReason = -1
5159
private var lastSubmittedLocation: Location? = null
5260
private var lastSubmittedCameraPosition: CameraPosition? = null
5361

@@ -112,6 +120,9 @@ class GoogleMapsViewImpl(
112120
googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl)
113121
googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl)
114122
googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl)
123+
googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl)
124+
googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
125+
googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
115126
googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
116127
}
117128
initLocationCallbacks()
@@ -282,6 +293,13 @@ class GoogleMapsViewImpl(
282293
}
283294
pendingPolygons.clear()
284295
}
296+
297+
if (pendingCircles.isNotEmpty()) {
298+
pendingCircles.forEach { (id, opts) ->
299+
internalAddCircle(id, opts)
300+
}
301+
pendingCircles.clear()
302+
}
285303
}
286304

287305
var buildingEnabled: Boolean? = null
@@ -388,6 +406,9 @@ class GoogleMapsViewImpl(
388406
var onLocationError: ((RNLocationErrorCode) -> Unit)? = null
389407
var onMapPress: ((RNLatLng) -> Unit)? = null
390408
var onMarkerPress: ((String) -> Unit)? = null
409+
var onPolylinePress: ((String) -> Unit)? = null
410+
var onPolygonPress: ((String) -> Unit)? = null
411+
var onCirclePress: ((String) -> Unit)? = null
391412
var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
392413
var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
393414
var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
@@ -669,18 +690,76 @@ class GoogleMapsViewImpl(
669690
pendingPolygons.clear()
670691
}
671692

693+
fun addCircle(
694+
id: String,
695+
opts: CircleOptions,
696+
) {
697+
if (googleMap == null) {
698+
pendingCircles.add(id to opts)
699+
return
700+
}
701+
702+
onUi {
703+
circlesById.remove(id)?.remove()
704+
}
705+
internalAddCircle(id, opts)
706+
}
707+
708+
private fun internalAddCircle(
709+
id: String,
710+
opts: CircleOptions,
711+
) {
712+
onUi {
713+
val circle =
714+
googleMap?.addCircle(opts).also {
715+
it?.tag = id
716+
}
717+
if (circle != null) {
718+
circlesById[id] = circle
719+
}
720+
}
721+
}
722+
723+
fun updateCircle(
724+
id: String,
725+
block: (Circle) -> Unit,
726+
) {
727+
val circle = circlesById[id] ?: return
728+
onUi {
729+
block(circle)
730+
}
731+
}
732+
733+
fun removeCircle(id: String) {
734+
onUi {
735+
circlesById.remove(id)?.remove()
736+
}
737+
}
738+
739+
fun clearCircles() {
740+
onUi {
741+
circlesById.values.forEach { it.remove() }
742+
}
743+
circlesById.clear()
744+
pendingCircles.clear()
745+
}
746+
672747
fun destroyInternal() {
673748
onUi {
674749
markerOptions.cancelAllJobs()
675750
clearMarkers()
676751
clearPolylines()
677752
clearPolygons()
753+
clearCircles()
678754
locationHandler.stop()
679755
googleMap?.apply {
680756
setOnCameraMoveStartedListener(null)
681757
setOnCameraMoveListener(null)
682758
setOnCameraIdleListener(null)
683759
setOnMarkerClickListener(null)
760+
setOnPolylineClickListener(null)
761+
setOnPolygonClickListener(null)
762+
setOnCircleClickListener(null)
684763
setOnMapClickListener(null)
685764
}
686765
googleMap = null
@@ -740,6 +819,18 @@ class GoogleMapsViewImpl(
740819
return true
741820
}
742821

822+
override fun onPolylineClick(polyline: Polyline) {
823+
onPolylinePress?.invoke(polyline.tag?.toString() ?: "unknown")
824+
}
825+
826+
override fun onPolygonClick(polygon: Polygon) {
827+
onPolygonPress?.invoke(polygon.tag?.toString() ?: "unknown")
828+
}
829+
830+
override fun onCircleClick(circle: Circle) {
831+
onCirclePress?.invoke(circle.tag?.toString() ?: "unknown")
832+
}
833+
743834
override fun onMapClick(coordinates: LatLng) {
744835
onMapPress?.invoke(
745836
RNLatLng(coordinates.latitude, coordinates.longitude),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.rngooglemapsplus
2+
3+
import com.facebook.react.uimanager.PixelUtil.dpToPx
4+
import com.google.android.gms.maps.model.CircleOptions
5+
import com.google.android.gms.maps.model.LatLng
6+
7+
class MapCircleOptions {
8+
fun buildCircleOptions(circle: RNCircle): CircleOptions =
9+
CircleOptions().apply {
10+
center(LatLng(circle.center.latitude, circle.center.longitude))
11+
circle.radius?.let { radius(it) }
12+
circle.strokeWidth?.let { strokeWidth(it.dpToPx()) }
13+
circle.strokeColor?.let { strokeColor(it.toColor()) }
14+
circle.fillColor?.let { fillColor(it.toColor()) }
15+
circle.pressable?.let { clickable(it) }
16+
circle.zIndex?.let { zIndex(it.toFloat()) }
17+
}
18+
}
19+
20+
fun RNCircle.circleEquals(b: RNCircle): Boolean {
21+
if (zIndex != b.zIndex) return false
22+
if (pressable != b.pressable) return false
23+
if (center != b.center) return false
24+
if (radius != b.radius) return false
25+
if (strokeWidth != b.strokeWidth) return false
26+
if (strokeColor != b.strokeColor) return false
27+
if (fillColor != b.fillColor) return false
28+
return true
29+
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ class MarkerOptions(
3636
m: RNMarker,
3737
icon: BitmapDescriptor,
3838
): MarkerOptions =
39-
MarkerOptions()
40-
.position(LatLng(m.coordinate.latitude, m.coordinate.longitude))
41-
.zIndex(m.zIndex.toFloat())
42-
.icon(icon)
43-
.anchor((m.anchor?.x ?: 0.5).toFloat(), (m.anchor?.y ?: 0.5).toFloat())
39+
MarkerOptions().apply {
40+
position(LatLng(m.coordinate.latitude, m.coordinate.longitude))
41+
anchor((m.anchor?.x ?: 0.5).toFloat(), (m.anchor?.y ?: 0.5).toFloat())
42+
icon(icon)
43+
m.zIndex?.let { zIndex(it.toFloat()) }
44+
}
4445

4546
fun buildIconAsync(
4647
id: String,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ class MapPolygonOptions {
1515
poly.fillColor?.let { fillColor(it.toColor()) }
1616
poly.strokeColor?.let { strokeColor(it.toColor()) }
1717
poly.strokeWidth?.let { strokeWidth(it.dpToPx()) }
18-
zIndex(poly.zIndex.toFloat())
18+
poly.pressable?.let { clickable(it) }
19+
poly.zIndex?.let { zIndex(it.toFloat()) }
1920
}
2021
}
2122

2223
fun RNPolygon.polygonEquals(b: RNPolygon): Boolean {
2324
if (zIndex != b.zIndex) return false
25+
if (pressable != b.pressable) return false
2426
if (strokeWidth != b.strokeWidth) return false
2527
if (fillColor != b.fillColor) return false
2628
if (strokeColor != b.strokeColor) return false

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class MapPolylineOptions {
2222
pl.lineCap?.let { endCap(mapLineCap(it)) }
2323
pl.lineJoin?.let { jointType(mapLineJoin(it)) }
2424
pl.color?.let { color(it.toColor()) }
25-
zIndex(pl.zIndex.toFloat())
25+
pl.pressable?.let { clickable(it) }
26+
pl.zIndex?.let { zIndex(it.toFloat()) }
2627
}
2728

2829
fun mapLineCap(type: RNLineCapType?): Cap =
@@ -43,6 +44,7 @@ class MapPolylineOptions {
4344

4445
fun RNPolyline.polylineEquals(b: RNPolyline): Boolean {
4546
if (zIndex != b.zIndex) return false
47+
if (pressable != b.pressable) return false
4648
if ((width ?: 0.0) != (b.width ?: 0.0)) return false
4749
if (lineCap != b.lineCap) return false
4850
if (lineJoin != b.lineJoin) return false

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

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.UiThreadUtil
55
import com.facebook.react.uimanager.PixelUtil.dpToPx
66
import com.facebook.react.uimanager.ThemedReactContext
77
import com.google.android.gms.maps.model.CameraPosition
8+
import com.google.android.gms.maps.model.LatLng
89
import com.google.android.gms.maps.model.MapColorScheme
910
import com.google.android.gms.maps.model.MapStyleOptions
1011
import com.margelo.nitro.core.Promise
@@ -21,6 +22,7 @@ class RNGoogleMapsPlusView(
2122
private val markerOptions = MarkerOptions()
2223
private val polylineOptions = MapPolylineOptions()
2324
private val polygonOptions = MapPolygonOptions()
25+
private val circleOptions = MapCircleOptions()
2426

2527
override val view =
2628
GoogleMapsViewImpl(context, locationHandler, playServiceHandler, markerOptions)
@@ -101,27 +103,24 @@ class RNGoogleMapsPlusView(
101103
} else if (!prev.markerEquals(next)) {
102104
view.updateMarker(id) { m ->
103105
onUi {
104-
if (prev.coordinate != next.coordinate) {
105-
m.position =
106-
com.google.android.gms.maps.model.LatLng(
107-
next.coordinate.latitude,
108-
next.coordinate.longitude,
109-
)
110-
}
111-
if (prev.zIndex != next.zIndex) {
112-
m.zIndex = next.zIndex.toFloat()
106+
m.position =
107+
LatLng(
108+
next.coordinate.latitude,
109+
next.coordinate.longitude,
110+
)
111+
next.zIndex?.let { m.zIndex = it.toFloat() } ?: run {
112+
m.zIndex = 0f
113113
}
114+
114115
if (!prev.markerStyleEquals(next)) {
115116
markerOptions.buildIconAsync(id, next) { icon ->
116117
m.setIcon(icon)
117118
}
118119
}
119-
if (prev.anchor != next.anchor) {
120-
m.setAnchor(
121-
(next.anchor?.x ?: 0.5).toFloat(),
122-
(next.anchor?.y ?: 0.5).toFloat(),
123-
)
124-
}
120+
m.setAnchor(
121+
(next.anchor?.x ?: 0.5).toFloat(),
122+
(next.anchor?.y ?: 0.5).toFloat(),
123+
)
125124
}
126125
}
127126
}
@@ -147,8 +146,8 @@ class RNGoogleMapsPlusView(
147146
onUi {
148147
gms.points =
149148
next.coordinates.map {
150-
com.google.android.gms.maps.model
151-
.LatLng(it.latitude, it.longitude)
149+
150+
LatLng(it.latitude, it.longitude)
152151
}
153152
next.width?.let { gms.width = it.dpToPx() }
154153
next.lineCap?.let {
@@ -158,7 +157,7 @@ class RNGoogleMapsPlusView(
158157
}
159158
next.lineJoin?.let { gms.jointType = polylineOptions.mapLineJoin(it) }
160159
next.color?.let { gms.color = it.toColor() }
161-
gms.zIndex = next.zIndex.toFloat()
160+
next.zIndex?.let { gms.zIndex = it.toFloat() }
162161
}
163162
}
164163
}
@@ -190,7 +189,36 @@ class RNGoogleMapsPlusView(
190189
next.fillColor?.let { gmsPoly.fillColor = it.toColor() }
191190
next.strokeColor?.let { gmsPoly.strokeColor = it.toColor() }
192191
next.strokeWidth?.let { gmsPoly.strokeWidth = it.dpToPx() }
193-
gmsPoly.zIndex = next.zIndex.toFloat()
192+
next.zIndex?.let { gmsPoly.zIndex = it.toFloat() }
193+
}
194+
}
195+
}
196+
}
197+
field = value
198+
}
199+
200+
override var circles: Array<RNCircle>? = null
201+
set(value) {
202+
val prevById = field?.associateBy { it.id } ?: emptyMap()
203+
val nextById = value?.associateBy { it.id } ?: emptyMap()
204+
205+
(prevById.keys - nextById.keys).forEach { id ->
206+
view.removeCircle(id)
207+
}
208+
209+
nextById.forEach { (id, next) ->
210+
val prev = prevById[id]
211+
if (prev == null) {
212+
view.addCircle(id, circleOptions.buildCircleOptions(next))
213+
} else if (!prev.circleEquals(next)) {
214+
view.updateCircle(id) { gmsCircle ->
215+
onUi {
216+
gmsCircle.center = LatLng(next.center.latitude, next.center.longitude)
217+
next.radius?.let { gmsCircle.radius = it }
218+
next.strokeWidth?.let { gmsCircle.strokeWidth = it.dpToPx() }
219+
next.strokeColor?.let { gmsCircle.strokeColor = it.toColor() }
220+
next.fillColor?.let { gmsCircle.fillColor = it.toColor() }
221+
next.zIndex?.let { gmsCircle.zIndex = it.toFloat() } ?: run { gmsCircle.zIndex = 0f }
194222
}
195223
}
196224
}
@@ -227,6 +255,21 @@ class RNGoogleMapsPlusView(
227255
view.onMarkerPress = cb
228256
}
229257

258+
override var onPolylinePress: ((String) -> Unit)? = null
259+
set(cb) {
260+
view.onPolylinePress = cb
261+
}
262+
263+
override var onPolygonPress: ((String) -> Unit)? = null
264+
set(cb) {
265+
view.onPolygonPress = cb
266+
}
267+
268+
override var onCirclePress: ((String) -> Unit)? = null
269+
set(cb) {
270+
view.onCirclePress = cb
271+
}
272+
230273
override var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
231274
set(cb) {
232275
view.onCameraChangeStart = cb

0 commit comments

Comments
 (0)