expo-gaode-map 2.2.30-next.0 → 2.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +4 -2
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +117 -57
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +8 -15
  4. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +20 -6
  5. package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +24 -13
  6. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +351 -0
  7. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +94 -310
  8. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
  9. package/build/ExpoGaodeMapModule.d.ts +13 -5
  10. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  11. package/build/ExpoGaodeMapModule.js +166 -34
  12. package/build/ExpoGaodeMapModule.js.map +1 -1
  13. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  14. package/build/ExpoGaodeMapView.js +12 -0
  15. package/build/ExpoGaodeMapView.js.map +1 -1
  16. package/build/components/AreaMaskOverlay.d.ts +5 -0
  17. package/build/components/AreaMaskOverlay.d.ts.map +1 -0
  18. package/build/components/AreaMaskOverlay.js +20 -0
  19. package/build/components/AreaMaskOverlay.js.map +1 -0
  20. package/build/components/FoldableMapView.d.ts.map +1 -1
  21. package/build/components/FoldableMapView.js +115 -104
  22. package/build/components/FoldableMapView.js.map +1 -1
  23. package/build/components/RouteOverlay.d.ts +5 -0
  24. package/build/components/RouteOverlay.d.ts.map +1 -0
  25. package/build/components/RouteOverlay.js +20 -0
  26. package/build/components/RouteOverlay.js.map +1 -0
  27. package/build/components/overlays/Cluster.d.ts.map +1 -1
  28. package/build/components/overlays/Cluster.js +12 -0
  29. package/build/components/overlays/Cluster.js.map +1 -1
  30. package/build/components/overlays/Marker.d.ts.map +1 -1
  31. package/build/components/overlays/Marker.js +86 -3
  32. package/build/components/overlays/Marker.js.map +1 -1
  33. package/build/hooks/useRoutePlayback.d.ts +4 -0
  34. package/build/hooks/useRoutePlayback.d.ts.map +1 -0
  35. package/build/hooks/useRoutePlayback.js +310 -0
  36. package/build/hooks/useRoutePlayback.js.map +1 -0
  37. package/build/index.d.ts +4 -1
  38. package/build/index.d.ts.map +1 -1
  39. package/build/index.js +4 -2
  40. package/build/index.js.map +1 -1
  41. package/build/types/common.types.d.ts +29 -5
  42. package/build/types/common.types.d.ts.map +1 -1
  43. package/build/types/common.types.js +5 -5
  44. package/build/types/common.types.js.map +1 -1
  45. package/build/types/index.d.ts +3 -2
  46. package/build/types/index.d.ts.map +1 -1
  47. package/build/types/index.js.map +1 -1
  48. package/build/types/location.types.d.ts +28 -0
  49. package/build/types/location.types.d.ts.map +1 -1
  50. package/build/types/location.types.js +5 -0
  51. package/build/types/location.types.js.map +1 -1
  52. package/build/types/map-view.types.d.ts +22 -22
  53. package/build/types/map-view.types.d.ts.map +1 -1
  54. package/build/types/map-view.types.js.map +1 -1
  55. package/build/types/native-module.types.d.ts +2 -2
  56. package/build/types/native-module.types.d.ts.map +1 -1
  57. package/build/types/native-module.types.js.map +1 -1
  58. package/build/types/overlays.types.d.ts +14 -0
  59. package/build/types/overlays.types.d.ts.map +1 -1
  60. package/build/types/overlays.types.js.map +1 -1
  61. package/build/types/route-playback.types.d.ts +118 -0
  62. package/build/types/route-playback.types.d.ts.map +1 -0
  63. package/build/types/route-playback.types.js +2 -0
  64. package/build/types/route-playback.types.js.map +1 -0
  65. package/build/utils/RouteUtils.d.ts +8 -0
  66. package/build/utils/RouteUtils.d.ts.map +1 -0
  67. package/build/utils/RouteUtils.js +140 -0
  68. package/build/utils/RouteUtils.js.map +1 -0
  69. package/ios/ExpoGaodeMapModule.swift +41 -22
  70. package/ios/ExpoGaodeMapView.swift +236 -241
  71. package/ios/ExpoGaodeMapViewModule.swift +16 -11
  72. package/ios/managers/UIManager.swift +5 -4
  73. package/ios/modules/LocationManager.swift +32 -9
  74. package/ios/overlays/ClusterView.swift +114 -12
  75. package/ios/overlays/ClusterViewModule.swift +5 -1
  76. package/ios/overlays/MarkerView.swift +195 -18
  77. package/ios/overlays/MarkerViewModule.swift +7 -7
  78. package/package.json +6 -6
  79. package/build/utils/throttle.d.ts +0 -10
  80. package/build/utils/throttle.d.ts.map +0 -1
  81. package/build/utils/throttle.js +0 -19
  82. package/build/utils/throttle.js.map +0 -1
@@ -772,7 +772,7 @@ class ExpoGaodeMapModule : Module() {
772
772
  // 使用 WeakReference 避免内存泄露
773
773
  val contextRef = java.lang.ref.WeakReference(appContext.reactContext)
774
774
  val handler = android.os.Handler(android.os.Looper.getMainLooper())
775
- val attempts = 0
775
+ var attempts = 0
776
776
  val maxAttempts = 50 // 增加到 5 秒 / 100ms,给用户足够时间操作
777
777
 
778
778
  val checkPermission = object : Runnable {
@@ -784,6 +784,7 @@ class ExpoGaodeMapModule : Module() {
784
784
  }
785
785
 
786
786
  val status = PermissionHelper.checkForegroundLocationPermission(context)
787
+ attempts += 1
787
788
 
788
789
  // 如果权限已授予或达到最大尝试次数,返回结果并清理 Handler
789
790
  if (status.granted || attempts >= maxAttempts) {
@@ -843,7 +844,7 @@ class ExpoGaodeMapModule : Module() {
843
844
  // 轮询检查权限状态
844
845
  val contextRef = java.lang.ref.WeakReference(appContext.reactContext)
845
846
  val handler = android.os.Handler(android.os.Looper.getMainLooper())
846
- val attempts = 0
847
+ var attempts = 0
847
848
  val maxAttempts = 30
848
849
 
849
850
  val checkPermission = object : Runnable {
@@ -855,6 +856,7 @@ class ExpoGaodeMapModule : Module() {
855
856
  }
856
857
 
857
858
  val status = PermissionHelper.checkBackgroundLocationPermission(context)
859
+ attempts += 1
858
860
 
859
861
  if (status.granted || attempts >= maxAttempts) {
860
862
  handler.removeCallbacks(this)
@@ -2,6 +2,7 @@ package expo.modules.gaodemap
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.os.SystemClock
5
6
  import android.view.View
6
7
  import android.view.ViewGroup
7
8
  import com.amap.api.maps.AMap
@@ -107,11 +108,17 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
107
108
 
108
109
  // Props 存储
109
110
  /** 地图类型 */
110
- internal var mapType: Int = 0
111
+ internal var mapType: Int = 1
111
112
  /** 初始相机位置 */
112
113
  internal var initialCameraPosition: Map<String, Any?>? = null
113
114
  /** 是否跟随用户位置 */
114
115
  internal var followUserLocation: Boolean = false
116
+ /** 是否显示底图文字标注 */
117
+ private var labelsEnabled: Boolean = true
118
+ /** 是否显示定位按钮 */
119
+ private var myLocationButtonEnabled: Boolean = false
120
+ /** 相机移动事件节流间隔 */
121
+ private var cameraEventThrottleMs: Long = 32L
115
122
  /** 自定义地图样式配置(缓存) */
116
123
  private var customMapStyleData: Map<String, Any>? = null
117
124
 
@@ -120,6 +127,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
120
127
 
121
128
  // 事件派发器
122
129
  private val onMapPress by EventDispatcher()
130
+ private val onPressPoi by EventDispatcher()
123
131
  private val onMapLongPress by EventDispatcher()
124
132
  private val onLoad by EventDispatcher()
125
133
  private val onLocation by EventDispatcher()
@@ -128,6 +136,8 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
128
136
 
129
137
  // 缓存的相机移动事件数据
130
138
  private var pendingCameraMoveData: Map<String, Any>? = null
139
+ private var pendingCameraMoveDispatch: Runnable? = null
140
+ private var lastCameraMoveDispatchAt: Long = 0L
131
141
 
132
142
  // 高德地图视图
133
143
  private lateinit var mapView: TextureMapView
@@ -176,7 +186,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
176
186
  isMapLoaded = true
177
187
 
178
188
  // 应用缓存的 Props
179
- if (mapType != 0) {
189
+ if (mapType != 1) {
180
190
  setMapType(mapType)
181
191
  }
182
192
 
@@ -191,6 +201,9 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
191
201
  uiManager.setCustomMapStyle(styleData)
192
202
  }
193
203
 
204
+ uiManager.setLabelsEnabled(labelsEnabled)
205
+ uiManager.setMyLocationButtonEnabled(myLocationButtonEnabled)
206
+
194
207
  onLoad(mapOf("loaded" to true))
195
208
  }
196
209
  } catch (_: Exception) {
@@ -223,39 +236,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
223
236
 
224
237
  // 相机移动中 - 应用节流优化
225
238
  cameraPosition?.let {
226
- val visibleRegion = aMap.projection.visibleRegion
227
- val eventData = mapOf(
228
- "cameraPosition" to mapOf(
229
- "target" to mapOf(
230
- "latitude" to it.target.latitude,
231
- "longitude" to it.target.longitude
232
- ),
233
- "zoom" to it.zoom,
234
- "tilt" to it.tilt,
235
- "bearing" to it.bearing
236
- ),
237
- "latLngBounds" to mapOf(
238
- "northeast" to mapOf(
239
- "latitude" to visibleRegion.farRight.latitude,
240
- "longitude" to visibleRegion.farRight.longitude
241
- ),
242
- "southwest" to mapOf(
243
- "latitude" to visibleRegion.nearLeft.latitude,
244
- "longitude" to visibleRegion.nearLeft.longitude
245
- )
246
- )
247
- )
248
-
249
- // 使用 onCameraMove 自身的节流机制(如果在 Module 定义中配置了 Coalescing)
250
- // 或者在这里简单发送,让 JS 端处理节流,或者依赖 Expo 的事件批处理
251
- // 这里我们移除自定义的 Handler 实现,直接发送事件,简化代码逻辑
252
- // 注意:高德地图的 onCameraChange 调用频率非常高,
253
- // 建议在 Module 定义中使用 Events("onCameraMove") 时考虑是否需要原生侧节流
254
- // 目前 Expo Modules 默认没有自动节流,但为了代码简洁和避免 Handler 泄漏风险,
255
- // 我们可以依赖 JS 端的 debounce/throttle,或者如果性能是瓶颈,再加回轻量级的节流。
256
- // 鉴于之前的 Handler 实现比较复杂且容易出错,我们先简化。
257
-
258
- onCameraMove(eventData)
239
+ dispatchCameraMoveEvent(buildCameraEventData(it))
259
240
  }
260
241
  }
261
242
 
@@ -265,28 +246,8 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
265
246
 
266
247
  // 相机移动完成
267
248
  cameraPosition?.let {
268
- val visibleRegion = aMap.projection.visibleRegion
269
- onCameraIdle(mapOf(
270
- "cameraPosition" to mapOf(
271
- "target" to mapOf(
272
- "latitude" to it.target.latitude,
273
- "longitude" to it.target.longitude
274
- ),
275
- "zoom" to it.zoom,
276
- "tilt" to it.tilt,
277
- "bearing" to it.bearing
278
- ),
279
- "latLngBounds" to mapOf(
280
- "northeast" to mapOf(
281
- "latitude" to visibleRegion.farRight.latitude,
282
- "longitude" to visibleRegion.farRight.longitude
283
- ),
284
- "southwest" to mapOf(
285
- "latitude" to visibleRegion.nearLeft.latitude,
286
- "longitude" to visibleRegion.nearLeft.longitude
287
- )
288
- )
289
- ))
249
+ flushPendingCameraMoveEvent()
250
+ onCameraIdle(buildCameraEventData(it))
290
251
  }
291
252
  }
292
253
  })
@@ -353,6 +314,17 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
353
314
  ))
354
315
  }
355
316
 
317
+ aMap.setOnPOIClickListener { poi ->
318
+ onPressPoi(mapOf(
319
+ "id" to poi.poiId,
320
+ "name" to poi.name,
321
+ "position" to mapOf(
322
+ "latitude" to poi.coordinate.latitude,
323
+ "longitude" to poi.coordinate.longitude
324
+ )
325
+ ))
326
+ }
327
+
356
328
  aMap.setOnMapLongClickListener { latLng ->
357
329
  onMapLongPress(mapOf(
358
330
  "latitude" to latLng.latitude,
@@ -456,6 +428,24 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
456
428
  fun setShowsBuildings(show: Boolean) = uiManager.setShowsBuildings(show)
457
429
  /** 设置是否显示室内地图 */
458
430
  fun setShowsIndoorMap(show: Boolean) = uiManager.setShowsIndoorMap(show)
431
+ /** 设置是否显示底图文字标注 */
432
+ fun setLabelsEnabled(enabled: Boolean) {
433
+ labelsEnabled = enabled
434
+ uiManager.setLabelsEnabled(enabled)
435
+ }
436
+ /** 设置是否显示定位按钮 */
437
+ fun setMyLocationButtonEnabled(enabled: Boolean) {
438
+ myLocationButtonEnabled = enabled
439
+ uiManager.setMyLocationButtonEnabled(enabled)
440
+ }
441
+ /** 设置相机移动事件节流间隔 */
442
+ fun setCameraEventThrottleMs(throttleMs: Int) {
443
+ cameraEventThrottleMs = throttleMs.toLong().coerceAtLeast(0L)
444
+ if (cameraEventThrottleMs == 0L) {
445
+ pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
446
+ pendingCameraMoveDispatch = null
447
+ }
448
+ }
459
449
 
460
450
  /**
461
451
  * 设置自定义地图样式
@@ -641,9 +631,12 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
641
631
  try {
642
632
  // 清理 Handler 回调,防止内存泄露
643
633
  mainHandler.removeCallbacksAndMessages(null)
634
+ pendingCameraMoveData = null
635
+ pendingCameraMoveDispatch = null
644
636
 
645
637
  // 清理所有地图监听器
646
638
  aMap.setOnMapClickListener(null)
639
+ aMap.setOnPOIClickListener(null)
647
640
  aMap.setOnMapLongClickListener(null)
648
641
  aMap.setOnMapLoadedListener(null)
649
642
  aMap.setOnCameraChangeListener(null)
@@ -663,6 +656,73 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
663
656
 
664
657
  }
665
658
 
659
+ private fun buildCameraEventData(cameraPosition: com.amap.api.maps.model.CameraPosition): Map<String, Any> {
660
+ val visibleRegion = aMap.projection.visibleRegion
661
+ return mapOf(
662
+ "cameraPosition" to mapOf(
663
+ "target" to mapOf(
664
+ "latitude" to cameraPosition.target.latitude,
665
+ "longitude" to cameraPosition.target.longitude
666
+ ),
667
+ "zoom" to cameraPosition.zoom,
668
+ "tilt" to cameraPosition.tilt,
669
+ "bearing" to cameraPosition.bearing
670
+ ),
671
+ "latLngBounds" to mapOf(
672
+ "northeast" to mapOf(
673
+ "latitude" to visibleRegion.farRight.latitude,
674
+ "longitude" to visibleRegion.farRight.longitude
675
+ ),
676
+ "southwest" to mapOf(
677
+ "latitude" to visibleRegion.nearLeft.latitude,
678
+ "longitude" to visibleRegion.nearLeft.longitude
679
+ )
680
+ )
681
+ )
682
+ }
683
+
684
+ private fun dispatchCameraMoveEvent(eventData: Map<String, Any>) {
685
+ val throttleMs = cameraEventThrottleMs.coerceAtLeast(0L)
686
+ if (throttleMs == 0L) {
687
+ pendingCameraMoveData = null
688
+ pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
689
+ pendingCameraMoveDispatch = null
690
+ lastCameraMoveDispatchAt = SystemClock.uptimeMillis()
691
+ onCameraMove(eventData)
692
+ return
693
+ }
694
+
695
+ pendingCameraMoveData = eventData
696
+
697
+ val now = SystemClock.uptimeMillis()
698
+ val elapsed = now - lastCameraMoveDispatchAt
699
+ if (elapsed >= throttleMs) {
700
+ pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
701
+ pendingCameraMoveDispatch = null
702
+ flushPendingCameraMoveEvent(now)
703
+ return
704
+ }
705
+
706
+ if (pendingCameraMoveDispatch != null) {
707
+ return
708
+ }
709
+
710
+ val delay = (throttleMs - elapsed).coerceAtLeast(0L)
711
+ val runnable = Runnable {
712
+ pendingCameraMoveDispatch = null
713
+ flushPendingCameraMoveEvent()
714
+ }
715
+ pendingCameraMoveDispatch = runnable
716
+ mainHandler.postDelayed(runnable, delay)
717
+ }
718
+
719
+ private fun flushPendingCameraMoveEvent(timestamp: Long = SystemClock.uptimeMillis()) {
720
+ val eventData = pendingCameraMoveData ?: return
721
+ pendingCameraMoveData = null
722
+ lastCameraMoveDispatchAt = timestamp
723
+ onCameraMove(eventData)
724
+ }
725
+
666
726
  /** 保存实例状态 */
667
727
  @Suppress("unused")
668
728
  fun onSaveInstanceState(outState: android.os.Bundle) {
@@ -3,16 +3,6 @@ package expo.modules.gaodemap
3
3
  import expo.modules.kotlin.modules.Module
4
4
  import expo.modules.kotlin.modules.ModuleDefinition
5
5
 
6
- import expo.modules.kotlin.types.Enumerable
7
-
8
- enum class MapType(val value: Int) : Enumerable {
9
- STANDARD(1),
10
- SATELLITE(2),
11
- NIGHT(3),
12
- NAVI(4),
13
- BUS(5)
14
- }
15
-
16
6
  /**
17
7
  * 高德地图视图 Module
18
8
  */
@@ -21,7 +11,7 @@ class ExpoGaodeMapViewModule : Module() {
21
11
  Name("ExpoGaodeMapView")
22
12
 
23
13
  View(ExpoGaodeMapView::class) {
24
- Events("onMapPress", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
14
+ Events("onMapPress", "onPressPoi", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
25
15
 
26
16
 
27
17
 
@@ -34,9 +24,9 @@ class ExpoGaodeMapViewModule : Module() {
34
24
  }
35
25
 
36
26
 
37
- Prop<MapType>("mapType") { view, type ->
38
- view.mapType = type.value
39
- view.setMapType(type.value)
27
+ Prop<Int>("mapType") { view, type ->
28
+ view.mapType = type
29
+ view.setMapType(type)
40
30
  }
41
31
 
42
32
  Prop<Map<String, Any?>?>("initialCameraPosition") { view, position ->
@@ -64,13 +54,16 @@ class ExpoGaodeMapViewModule : Module() {
64
54
  Prop<Boolean>("trafficEnabled") { view, show -> view.setShowsTraffic(show) }
65
55
  Prop<Boolean>("buildingsEnabled") { view, show -> view.setShowsBuildings(show) }
66
56
  Prop<Boolean>("indoorViewEnabled") { view, show -> view.setShowsIndoorMap(show) }
57
+ Prop<Boolean>("labelsEnabled") { view, enabled -> view.setLabelsEnabled(enabled) }
58
+ Prop<Boolean>("myLocationButtonEnabled") { view, enabled -> view.setMyLocationButtonEnabled(enabled) }
59
+ Prop<Int>("cameraEventThrottleMs") { view, throttleMs -> view.setCameraEventThrottleMs(throttleMs) }
67
60
 
68
61
  Prop<Map<String, Any>?>("customMapStyle") { view, styleData ->
69
62
  styleData?.let { view.setCustomMapStyle(it) }
70
63
  }
71
64
 
72
65
  OnViewDidUpdateProps { view: ExpoGaodeMapView ->
73
- if (view.mapType != 0) {
66
+ if (view.mapType != 1) {
74
67
  view.setMapType(view.mapType)
75
68
  }
76
69
 
@@ -46,6 +46,13 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
46
46
  fun setShowsScale(show: Boolean) {
47
47
  aMap.uiSettings.isScaleControlsEnabled = show
48
48
  }
49
+
50
+ /**
51
+ * 设置是否显示定位按钮
52
+ */
53
+ fun setMyLocationButtonEnabled(enabled: Boolean) {
54
+ aMap.uiSettings.isMyLocationButtonEnabled = enabled
55
+ }
49
56
 
50
57
  // ==================== 手势控制 ====================
51
58
 
@@ -322,7 +329,7 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
322
329
  fun setShowsTraffic(show: Boolean) {
323
330
  aMap.isTrafficEnabled = show
324
331
  }
325
-
332
+
326
333
  /**
327
334
  * 设置是否显示建筑物
328
335
  */
@@ -336,17 +343,24 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
336
343
  fun setShowsIndoorMap(show: Boolean) {
337
344
  aMap.showIndoorMap(show)
338
345
  }
346
+
347
+ /**
348
+ * 设置是否显示底图文字标注
349
+ */
350
+ fun setLabelsEnabled(enabled: Boolean) {
351
+ aMap.showMapText(enabled)
352
+ }
339
353
 
340
354
  /**
341
355
  * 设置地图类型
342
356
  */
343
357
  fun setMapType(type: Int) {
344
358
  aMap.mapType = when (type) {
345
- 1 -> AMap.MAP_TYPE_SATELLITE // 卫星地图
346
- 2 -> AMap.MAP_TYPE_NIGHT // 夜间地图
347
- 3 -> AMap.MAP_TYPE_NAVI // 导航地图
348
- 4 -> AMap.MAP_TYPE_BUS // 公交地图
349
- else -> AMap.MAP_TYPE_NORMAL // 标准地图
359
+ 2 -> AMap.MAP_TYPE_SATELLITE // 卫星地图
360
+ 3 -> AMap.MAP_TYPE_NIGHT // 夜间地图
361
+ 4 -> AMap.MAP_TYPE_NAVI // 导航地图
362
+ 5 -> AMap.MAP_TYPE_BUS // 公交地图
363
+ else -> AMap.MAP_TYPE_NORMAL // 标准地图 (1, 以及兼容旧值 0)
350
364
  }
351
365
  }
352
366
 
@@ -2,7 +2,6 @@ package expo.modules.gaodemap.overlays
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
- import android.util.Log
6
5
  import android.graphics.Bitmap
7
6
  import android.graphics.BitmapFactory
8
7
  import android.graphics.Canvas
@@ -64,7 +63,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
64
63
 
65
64
  }
66
65
 
67
- private var rawPoints: List<Map<String, Any>> = emptyList()
68
66
  private var clusterItems: List<ClusterItem> = emptyList()
69
67
  private var clusters: List<Cluster> = emptyList()
70
68
 
@@ -91,6 +89,7 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
91
89
  private var currentIconDescriptor: BitmapDescriptor? = null
92
90
  private var customIconBitmap: Bitmap? = null
93
91
  private var pendingIconUri: String? = null
92
+ private var pendingRetryUpdate: Runnable? = null
94
93
 
95
94
  // 标记样式是否发生变化,用于强制更新图标
96
95
  private var styleChanged = false
@@ -117,7 +116,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
117
116
  * 设置聚合点
118
117
  */
119
118
  fun setPoints(points: List<Map<String, Any>>) {
120
- rawPoints = points
121
119
  clusterItems = points.mapNotNull { pointData ->
122
120
  LatLngParser.parseLatLng(pointData)?.let { latLng ->
123
121
  ClusterItem(latLng, pointData)
@@ -141,7 +139,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
141
139
  * 设置最小聚合数量
142
140
  */
143
141
  fun setMinClusterSize(size: Int) {
144
- Log.d("ClusterView", "setMinClusterSize: $size")
145
142
  minClusterSize = size
146
143
  updateClusters()
147
144
  }
@@ -227,8 +224,8 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
227
224
  updateClusters()
228
225
  }
229
226
  }
230
- } catch (e: Exception) {
231
- e.printStackTrace()
227
+ } catch (_: Exception) {
228
+ // 忽略异常,保留当前默认图标
232
229
  }
233
230
  }
234
231
  }
@@ -265,6 +262,8 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
265
262
  }
266
263
  currentMarkers.clear()
267
264
  bitmapCache.clear()
265
+ pendingRetryUpdate?.let { mainHandler.removeCallbacks(it) }
266
+ pendingRetryUpdate = null
268
267
  }
269
268
 
270
269
  /**
@@ -283,7 +282,12 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
283
282
  }
284
283
 
285
284
  calculationJob = scope.launch(Dispatchers.Default) {
286
- if (clusterItems.isEmpty()) return@launch
285
+ if (clusterItems.isEmpty()) {
286
+ withContext(Dispatchers.Main) {
287
+ renderClusters(emptyList())
288
+ }
289
+ return@launch
290
+ }
287
291
 
288
292
  // 获取当前比例尺 (米/像素)
289
293
  val scalePerPixel = withContext(Dispatchers.Main) {
@@ -296,11 +300,7 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
296
300
  }
297
301
 
298
302
  if (scalePerPixel <= 0) {
299
- // 比例尺无效,稍后重试
300
- withContext(Dispatchers.Main) {
301
- Log.w("ClusterView", "Invalid scalePerPixel: $scalePerPixel, retrying...")
302
- mainHandler.postDelayed({ updateClusters() }, 500)
303
- }
303
+ scheduleRetryUpdate()
304
304
  return@launch
305
305
  }
306
306
 
@@ -319,6 +319,18 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
319
319
  }
320
320
  }
321
321
 
322
+ private fun scheduleRetryUpdate(delayMs: Long = 500L) {
323
+ pendingRetryUpdate?.let { mainHandler.removeCallbacks(it) }
324
+ val retryTask = Runnable {
325
+ pendingRetryUpdate = null
326
+ if (aMap != null) {
327
+ updateClusters()
328
+ }
329
+ }
330
+ pendingRetryUpdate = retryTask
331
+ mainHandler.postDelayed(retryTask, delayMs)
332
+ }
333
+
322
334
  private fun buildClustersFromNative(radiusMeters: Double): List<Cluster>? {
323
335
  return try {
324
336
  val latitudes = DoubleArray(clusterItems.size)
@@ -396,7 +408,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
396
408
  * 使用 Diff 算法优化渲染,避免全量刷新导致的闪烁
397
409
  */
398
410
  private fun renderClusters(newClusters: List<Cluster>) {
399
- Log.d("ClusterView", "renderClusters: count=${newClusters.size}, minClusterSize=$minClusterSize")
400
411
  val map = aMap ?: return
401
412
  clusters = newClusters
402
413