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

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 (103) hide show
  1. package/README.md +3 -4
  2. package/android/src/main/cpp/cluster_jni.cpp +56 -0
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +45 -6
  4. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapOfflineModule.kt +1 -1
  5. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +2 -2
  6. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +1 -1
  7. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +5 -5
  8. package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +23 -17
  9. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +30 -16
  10. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +36 -31
  11. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +6 -6
  12. package/android/src/main/java/expo/modules/gaodemap/utils/GeometryUtils.kt +103 -0
  13. package/build/ExpoGaodeMapModule.d.ts +9 -259
  14. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  15. package/build/ExpoGaodeMapModule.js +19 -927
  16. package/build/ExpoGaodeMapModule.js.map +1 -1
  17. package/build/ExpoGaodeMapView.d.ts +3 -4
  18. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  19. package/build/ExpoGaodeMapView.js +28 -25
  20. package/build/ExpoGaodeMapView.js.map +1 -1
  21. package/build/components/overlays/Circle.d.ts.map +1 -1
  22. package/build/components/overlays/Circle.js +1 -30
  23. package/build/components/overlays/Circle.js.map +1 -1
  24. package/build/components/overlays/Cluster.d.ts.map +1 -1
  25. package/build/components/overlays/Cluster.js +1 -42
  26. package/build/components/overlays/Cluster.js.map +1 -1
  27. package/build/components/overlays/HeatMap.d.ts.map +1 -1
  28. package/build/components/overlays/HeatMap.js +1 -20
  29. package/build/components/overlays/HeatMap.js.map +1 -1
  30. package/build/components/overlays/Marker.d.ts.map +1 -1
  31. package/build/components/overlays/Marker.js +14 -80
  32. package/build/components/overlays/Marker.js.map +1 -1
  33. package/build/components/overlays/Polygon.d.ts.map +1 -1
  34. package/build/components/overlays/Polygon.js +1 -25
  35. package/build/components/overlays/Polygon.js.map +1 -1
  36. package/build/components/overlays/Polyline.d.ts.map +1 -1
  37. package/build/components/overlays/Polyline.js +1 -31
  38. package/build/components/overlays/Polyline.js.map +1 -1
  39. package/build/hooks/useRoutePlayback.d.ts.map +1 -1
  40. package/build/hooks/useRoutePlayback.js +83 -43
  41. package/build/hooks/useRoutePlayback.js.map +1 -1
  42. package/build/index.d.ts +6 -2
  43. package/build/index.d.ts.map +1 -1
  44. package/build/index.js +6 -2
  45. package/build/index.js.map +1 -1
  46. package/build/module/geometry.d.ts +183 -0
  47. package/build/module/geometry.d.ts.map +1 -0
  48. package/build/module/geometry.js +374 -0
  49. package/build/module/geometry.js.map +1 -0
  50. package/build/module/location.d.ts +49 -0
  51. package/build/module/location.d.ts.map +1 -0
  52. package/build/module/location.js +257 -0
  53. package/build/module/location.js.map +1 -0
  54. package/build/module/nativeModule.d.ts +5 -0
  55. package/build/module/nativeModule.d.ts.map +1 -0
  56. package/build/module/nativeModule.js +34 -0
  57. package/build/module/nativeModule.js.map +1 -0
  58. package/build/module/normalizers.d.ts +9 -0
  59. package/build/module/normalizers.d.ts.map +1 -0
  60. package/build/module/normalizers.js +109 -0
  61. package/build/module/normalizers.js.map +1 -0
  62. package/build/module/privacy.d.ts +20 -0
  63. package/build/module/privacy.d.ts.map +1 -0
  64. package/build/module/privacy.js +59 -0
  65. package/build/module/privacy.js.map +1 -0
  66. package/build/module/sdk.d.ts +20 -0
  67. package/build/module/sdk.d.ts.map +1 -0
  68. package/build/module/sdk.js +82 -0
  69. package/build/module/sdk.js.map +1 -0
  70. package/build/types/common.types.d.ts +5 -5
  71. package/build/types/common.types.js +5 -5
  72. package/build/types/common.types.js.map +1 -1
  73. package/build/types/index.d.ts +1 -1
  74. package/build/types/index.js.map +1 -1
  75. package/build/types/native-module.types.d.ts +12 -16
  76. package/build/types/native-module.types.d.ts.map +1 -1
  77. package/build/types/native-module.types.js.map +1 -1
  78. package/build/types/overlays.types.d.ts +0 -16
  79. package/build/types/overlays.types.d.ts.map +1 -1
  80. package/build/types/overlays.types.js.map +1 -1
  81. package/build/types/route-playback.types.d.ts +3 -0
  82. package/build/types/route-playback.types.d.ts.map +1 -1
  83. package/build/types/route-playback.types.js.map +1 -1
  84. package/build/utils/RouteUtils.d.ts +1 -1
  85. package/build/utils/RouteUtils.d.ts.map +1 -1
  86. package/build/utils/RouteUtils.js +35 -1
  87. package/build/utils/RouteUtils.js.map +1 -1
  88. package/ios/ExpoGaodeMapModule.swift +47 -10
  89. package/ios/ExpoGaodeMapView.swift +33 -12
  90. package/ios/ExpoGaodeMapViewModule.swift +2 -10
  91. package/ios/GaodeMapPrivacyManager.swift +26 -18
  92. package/ios/managers/UIManager.swift +5 -4
  93. package/ios/modules/LocationManager.swift +18 -1
  94. package/ios/overlays/MarkerView.swift +36 -25
  95. package/ios/overlays/MarkerViewModule.swift +4 -4
  96. package/ios/utils/ClusterNative.h +8 -0
  97. package/ios/utils/ClusterNative.mm +27 -0
  98. package/ios/utils/PermissionManager.swift +11 -6
  99. package/package.json +15 -2
  100. package/scripts/check-expo-modules.js +68 -0
  101. package/shared/cpp/GeometryEngine.cpp +112 -0
  102. package/shared/cpp/GeometryEngine.hpp +21 -0
  103. package/shared/cpp/tests/test_main.cpp +15 -0
package/README.md CHANGED
@@ -32,7 +32,6 @@
32
32
  - ✅ 默认实现了优化地图加载,减少内存占用
33
33
  - ✅ 几何运算(距离/面积、点在圆/多边形、质心/边界、路径长度/抽稀、GeoHash、瓦片/像素坐标转换、最近点、热力网格等,由 C++ 实现)
34
34
  - ✅ 丰富的使用案例
35
- - ✅ 提供AI编程助手,帮助开发者快速集成和使用(https://TomWq.github.io/expo-gaode-map/guide/ai-skills.html)
36
35
  - ✅ 更多内容和功能请查看 [完整文档](https://TomWq.github.io/expo-gaode-map/)
37
36
 
38
37
 
@@ -48,9 +47,9 @@
48
47
  > - 如果你的项目使用 **Expo SDK 54 及以上**,请安装 默认的 版本。
49
48
  > - 如果你的项目使用 **Expo SDK 53 及以下**(如 50, 51, 52, 53),请使用 **V1** 版本(Tag: `v1`)。
50
49
  > ```bash
51
- > npm install expo-gaode-map@v1
50
+ > npm install expo-gaode-map@v1.2.3
52
51
  > ```
53
- > **说明**:V1 版本除了不支持**世界地图**功能外,其余 API V2 (Latest) 版本完全一致。
52
+ > **说明**:V1 版本不支持世界地图,只是为了兼容佬项目使用,请尽快升级你的 expo 版本到 54 及以上,使用最新的 V2 版本。
54
53
 
55
54
  ### 方案一:仅使用地图和定位(核心包)
56
55
 
@@ -252,4 +251,4 @@ MIT
252
251
  - 📝 提交 [GitHub Issue](https://github.com/TomWq/expo-gaode-map/issues)
253
252
  - 💬 参与 [Discussions](https://github.com/TomWq/expo-gaode-map/discussions)
254
253
  - ⭐ 给项目点个 Star 支持一下
255
- - 群:952241387 (欢迎加入,交流使用经验、问题反馈等)
254
+ - QQ:582752848
@@ -606,6 +606,62 @@ Java_expo_modules_gaodemap_utils_GeometryUtils_nativeCalculatePathBounds(
606
606
  #endif
607
607
  }
608
608
 
609
+ extern "C" JNIEXPORT jdouble JNICALL
610
+ Java_expo_modules_gaodemap_utils_GeometryUtils_nativeCalculateFitZoom(
611
+ JNIEnv* env,
612
+ jclass,
613
+ jdoubleArray latitudes,
614
+ jdoubleArray longitudes,
615
+ jdouble viewportWidthPx,
616
+ jdouble viewportHeightPx,
617
+ jdouble paddingPx,
618
+ jint minZoom,
619
+ jint maxZoom
620
+ ) {
621
+ #if GAODE_HAVE_JNI
622
+ if (!latitudes || !longitudes) {
623
+ return static_cast<jdouble>(minZoom);
624
+ }
625
+
626
+ const jsize countLat = env->GetArrayLength(latitudes);
627
+ const jsize countLon = env->GetArrayLength(longitudes);
628
+ if (countLat == 0 || countLat != countLon) {
629
+ return static_cast<jdouble>(minZoom);
630
+ }
631
+
632
+ jdouble* latValues = env->GetDoubleArrayElements(latitudes, nullptr);
633
+ jdouble* lonValues = env->GetDoubleArrayElements(longitudes, nullptr);
634
+
635
+ std::vector<gaodemap::GeoPoint> points;
636
+ points.reserve(static_cast<size_t>(countLat));
637
+ for (jsize i = 0; i < countLat; ++i) {
638
+ points.push_back({latValues[i], lonValues[i]});
639
+ }
640
+
641
+ env->ReleaseDoubleArrayElements(latitudes, latValues, JNI_ABORT);
642
+ env->ReleaseDoubleArrayElements(longitudes, lonValues, JNI_ABORT);
643
+
644
+ return static_cast<jdouble>(gaodemap::calculateFitZoomForPoints(
645
+ points,
646
+ static_cast<double>(viewportWidthPx),
647
+ static_cast<double>(viewportHeightPx),
648
+ static_cast<double>(paddingPx),
649
+ static_cast<int>(minZoom),
650
+ static_cast<int>(maxZoom)
651
+ ));
652
+ #else
653
+ (void)env;
654
+ (void)latitudes;
655
+ (void)longitudes;
656
+ (void)viewportWidthPx;
657
+ (void)viewportHeightPx;
658
+ (void)paddingPx;
659
+ (void)minZoom;
660
+ (void)maxZoom;
661
+ return 3.0;
662
+ #endif
663
+ }
664
+
609
665
  extern "C" JNIEXPORT jdoubleArray JNICALL
610
666
  Java_expo_modules_gaodemap_utils_GeometryUtils_nativeCalculateCentroid(
611
667
  JNIEnv* env,
@@ -51,7 +51,7 @@ class ExpoGaodeMapModule : Module() {
51
51
  } else if (!SDKInitializer.isPrivacyReady()) {
52
52
  throw expo.modules.kotlin.exception.CodedException(
53
53
  "PRIVACY_NOT_AGREED",
54
- "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
54
+ "隐私协议未完成确认,请先调用 setPrivacyConfig",
55
55
  null
56
56
  )
57
57
  } else {
@@ -69,12 +69,20 @@ class ExpoGaodeMapModule : Module() {
69
69
  }
70
70
  }
71
71
 
72
- Function("setPrivacyShow") { hasShow: Boolean, hasContainsPrivacy: Boolean ->
73
- SDKInitializer.setPrivacyShow(appContext.reactContext!!, hasShow, hasContainsPrivacy)
74
- }
72
+ Function("setPrivacyConfig") { config: Map<String, Any?> ->
73
+ val hasShow = config["hasShow"] as? Boolean ?: false
74
+ val hasContainsPrivacy = config["hasContainsPrivacy"] as? Boolean ?: hasShow
75
+ val hasAgree = config["hasAgree"] as? Boolean ?: false
76
+ val privacyVersion = config["privacyVersion"] as? String
75
77
 
76
- Function("setPrivacyAgree") { hasAgree: Boolean ->
77
- SDKInitializer.setPrivacyAgree(appContext.reactContext!!, hasAgree)
78
+ SDKInitializer.setPrivacyConfig(
79
+ appContext.reactContext!!,
80
+ hasShow,
81
+ hasContainsPrivacy,
82
+ hasAgree,
83
+ privacyVersion,
84
+ config.containsKey("privacyVersion")
85
+ )
78
86
  }
79
87
 
80
88
  Function("setPrivacyVersion") { version: String ->
@@ -187,6 +195,37 @@ class ExpoGaodeMapModule : Module() {
187
195
  }
188
196
  }
189
197
 
198
+ /**
199
+ * 根据多个坐标点计算可同时可见的推荐缩放级别
200
+ * @param points 坐标点(至少 1 个)
201
+ * @param viewportWidthPx 视口宽度(像素)
202
+ * @param viewportHeightPx 视口高度(像素)
203
+ * @param paddingPx 内边距(像素)
204
+ * @param minZoom 最小缩放
205
+ * @param maxZoom 最大缩放
206
+ */
207
+ Function("calculateFitZoom") {
208
+ points: List<Any>?,
209
+ viewportWidthPx: Double?,
210
+ viewportHeightPx: Double?,
211
+ paddingPx: Double?,
212
+ minZoom: Int?,
213
+ maxZoom: Int? ->
214
+ val normalized = LatLngParser.parseLatLngList(points)
215
+ val safeMinZoom = minZoom ?: 3
216
+ val safeMaxZoom = maxZoom ?: 20
217
+ if (normalized.isEmpty()) return@Function safeMinZoom.toDouble()
218
+
219
+ GeometryUtils.calculateFitZoom(
220
+ normalized,
221
+ viewportWidthPx ?: 390.0,
222
+ viewportHeightPx ?: 844.0,
223
+ paddingPx ?: 48.0,
224
+ safeMinZoom,
225
+ safeMaxZoom
226
+ )
227
+ }
228
+
190
229
  /**
191
230
  * 计算多边形面积
192
231
  * @param points 多边形顶点坐标数组,支持嵌套数组(多边形空洞)
@@ -42,7 +42,7 @@ class ExpoGaodeMapOfflineModule : Module() {
42
42
  if (!SDKInitializer.isPrivacyReady()) {
43
43
  throw CodedException(
44
44
  "PRIVACY_NOT_AGREED",
45
- "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
45
+ "隐私协议未完成确认,请先调用 setPrivacyConfig",
46
46
  null
47
47
  )
48
48
  }
@@ -108,7 +108,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
108
108
 
109
109
  // Props 存储
110
110
  /** 地图类型 */
111
- internal var mapType: Int = 0
111
+ internal var mapType: Int = 1
112
112
  /** 初始相机位置 */
113
113
  internal var initialCameraPosition: Map<String, Any?>? = null
114
114
  /** 是否跟随用户位置 */
@@ -186,7 +186,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
186
186
  isMapLoaded = true
187
187
 
188
188
  // 应用缓存的 Props
189
- if (mapType != 0) {
189
+ if (mapType != 1) {
190
190
  setMapType(mapType)
191
191
  }
192
192
 
@@ -63,7 +63,7 @@ class ExpoGaodeMapViewModule : Module() {
63
63
  }
64
64
 
65
65
  OnViewDidUpdateProps { view: ExpoGaodeMapView ->
66
- if (view.mapType != 0) {
66
+ if (view.mapType != 1) {
67
67
  view.setMapType(view.mapType)
68
68
  }
69
69
 
@@ -356,11 +356,11 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
356
356
  */
357
357
  fun setMapType(type: Int) {
358
358
  aMap.mapType = when (type) {
359
- 1 -> AMap.MAP_TYPE_SATELLITE // 卫星地图
360
- 2 -> AMap.MAP_TYPE_NIGHT // 夜间地图
361
- 3 -> AMap.MAP_TYPE_NAVI // 导航地图
362
- 4 -> AMap.MAP_TYPE_BUS // 公交地图
363
- 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)
364
364
  }
365
365
  }
366
366
 
@@ -56,22 +56,6 @@ object SDKInitializer {
56
56
  applyPrivacyState(appContext)
57
57
  }
58
58
 
59
- fun setPrivacyShow(context: Context, hasShow: Boolean, hasContainsPrivacy: Boolean) {
60
- privacyShown = hasShow
61
- privacyContains = hasContainsPrivacy
62
- val appContext = resolveContext(context)
63
- persistState(appContext)
64
- applyPrivacyState(appContext)
65
- }
66
-
67
- fun setPrivacyAgree(context: Context, hasAgree: Boolean) {
68
- privacyAgreed = hasAgree
69
- agreedPrivacyVersion = if (hasAgree) privacyVersion else null
70
- val appContext = resolveContext(context)
71
- persistState(appContext)
72
- applyPrivacyState(appContext)
73
- }
74
-
75
59
  fun setPrivacyVersion(context: Context, version: String) {
76
60
  val sanitizedVersion = version.trim().takeIf { it.isNotEmpty() }
77
61
  privacyVersion = sanitizedVersion
@@ -89,6 +73,28 @@ object SDKInitializer {
89
73
  applyPrivacyState(appContext)
90
74
  }
91
75
 
76
+ fun setPrivacyConfig(
77
+ context: Context,
78
+ hasShow: Boolean,
79
+ hasContainsPrivacy: Boolean,
80
+ hasAgree: Boolean,
81
+ version: String?,
82
+ shouldUpdateVersion: Boolean
83
+ ) {
84
+ val appContext = resolveContext(context)
85
+
86
+ if (shouldUpdateVersion) {
87
+ privacyVersion = version?.trim()?.takeIf { it.isNotEmpty() }
88
+ }
89
+ privacyShown = hasShow
90
+ privacyContains = hasContainsPrivacy
91
+ privacyAgreed = hasAgree
92
+ agreedPrivacyVersion = if (hasAgree) privacyVersion else null
93
+
94
+ persistState(appContext)
95
+ applyPrivacyState(appContext)
96
+ }
97
+
92
98
  fun resetPrivacyConsent(context: Context) {
93
99
  val appContext = resolveContext(context)
94
100
  clearConsentPersistedState(appContext, keepCurrentVersion = false)
@@ -137,7 +143,7 @@ object SDKInitializer {
137
143
  if (!isPrivacyReady()) {
138
144
  throw expo.modules.kotlin.exception.CodedException(
139
145
  "PRIVACY_NOT_AGREED",
140
- "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
146
+ "隐私协议未完成确认,请先调用 setPrivacyConfig",
141
147
  null
142
148
  )
143
149
  }
@@ -51,8 +51,8 @@ internal object MarkerBitmapRenderer {
51
51
 
52
52
  fun resolveSnapshot(
53
53
  container: ViewGroup,
54
- customViewWidth: Int,
55
- customViewHeight: Int,
54
+ contentWidth: Int,
55
+ contentHeight: Int,
56
56
  cacheKey: String?,
57
57
  ): MarkerBitmapSnapshot? {
58
58
  if (container.childCount == 0) {
@@ -76,17 +76,19 @@ internal object MarkerBitmapRenderer {
76
76
  ?: child.measuredHeight
77
77
  ?: 0
78
78
 
79
- val finalWidth = if (measuredWidth > 0) measuredWidth else customViewWidth
80
- val finalHeight = if (measuredHeight > 0) measuredHeight else customViewHeight
79
+ val finalWidth = if (measuredWidth > 0) measuredWidth else contentWidth
80
+ val finalHeight = if (measuredHeight > 0) measuredHeight else contentHeight
81
81
 
82
82
  if (finalWidth <= 0 || finalHeight <= 0) {
83
83
  return null
84
84
  }
85
85
 
86
- val fingerprint = computeViewFingerprint(container)
86
+ // 业务层已经显式传入稳定 cacheKey 时,直接信任它。
87
+ // 否则点击、重排或 drawable 实例抖动仍会让 key 变化,导致 children marker 反复重建。
88
+ val keyPart = cacheKey?.takeIf { it.isNotBlank() } ?: computeViewFingerprint(container)
87
89
 
88
90
  return MarkerBitmapSnapshot(
89
- keyPart = cacheKey?.let { "$it|$fingerprint" } ?: fingerprint,
91
+ keyPart = keyPart,
90
92
  width = finalWidth,
91
93
  height = finalHeight,
92
94
  )
@@ -95,8 +97,8 @@ internal object MarkerBitmapRenderer {
95
97
  fun createBitmap(
96
98
  container: ViewGroup,
97
99
  snapshot: MarkerBitmapSnapshot,
98
- customViewWidth: Int,
99
- customViewHeight: Int,
100
+ contentWidth: Int,
101
+ contentHeight: Int,
100
102
  mainHandler: Handler,
101
103
  ): Bitmap? {
102
104
  IconBitmapCache.get(snapshot.fullCacheKey)?.let { return it }
@@ -107,8 +109,8 @@ internal object MarkerBitmapRenderer {
107
109
  container = container,
108
110
  finalWidth = snapshot.width,
109
111
  finalHeight = snapshot.height,
110
- customViewWidth = customViewWidth,
111
- customViewHeight = customViewHeight,
112
+ contentWidth = contentWidth,
113
+ contentHeight = contentHeight,
112
114
  )
113
115
  } else {
114
116
  val latch = CountDownLatch(1)
@@ -120,8 +122,8 @@ internal object MarkerBitmapRenderer {
120
122
  container = container,
121
123
  finalWidth = snapshot.width,
122
124
  finalHeight = snapshot.height,
123
- customViewWidth = customViewWidth,
124
- customViewHeight = customViewHeight,
125
+ contentWidth = contentWidth,
126
+ contentHeight = contentHeight,
125
127
  )
126
128
  } finally {
127
129
  latch.countDown()
@@ -227,7 +229,19 @@ internal object MarkerBitmapRenderer {
227
229
  } else {
228
230
  val drawable = v.drawable
229
231
  if (drawable != null) {
230
- sb.append("[drawableHash=").append(drawable.hashCode()).append("]")
232
+ val constantState = drawable.constantState
233
+ if (constantState != null) {
234
+ sb.append("[drawableState=").append(constantState.hashCode()).append("]")
235
+ } else {
236
+ sb
237
+ .append("[drawable=")
238
+ .append(drawable.javaClass.simpleName)
239
+ .append(':')
240
+ .append(drawable.intrinsicWidth)
241
+ .append('x')
242
+ .append(drawable.intrinsicHeight)
243
+ .append("]")
244
+ }
231
245
  }
232
246
  }
233
247
  }
@@ -248,8 +262,8 @@ internal object MarkerBitmapRenderer {
248
262
  container: ViewGroup,
249
263
  finalWidth: Int,
250
264
  finalHeight: Int,
251
- customViewWidth: Int,
252
- customViewHeight: Int,
265
+ contentWidth: Int,
266
+ contentHeight: Int,
253
267
  ): Bitmap? {
254
268
  try {
255
269
  val childView = container.getChildAt(0) ?: return null
@@ -271,7 +285,7 @@ internal object MarkerBitmapRenderer {
271
285
  val canvas = Canvas(bitmap)
272
286
  childView.draw(canvas)
273
287
 
274
- val shouldTrimTransparentPadding = customViewWidth <= 0 && customViewHeight <= 0
288
+ val shouldTrimTransparentPadding = contentWidth <= 0 && contentHeight <= 0
275
289
  return if (shouldTrimTransparentPadding) trimTransparentPadding(bitmap) else bitmap
276
290
  } catch (_: Exception) {
277
291
  return null
@@ -82,16 +82,16 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
82
82
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
83
83
  val selfParams = this.layoutParams
84
84
  if (selfParams == null || selfParams !is LayoutParams) {
85
- val width = if (customViewWidth > 0) {
86
- customViewWidth
85
+ val width = if (contentWidth > 0) {
86
+ contentWidth
87
87
  } else if (selfParams != null && selfParams.width > 0) {
88
88
  selfParams.width
89
89
  } else {
90
90
  LayoutParams.WRAP_CONTENT
91
91
  }
92
92
 
93
- val height = if (customViewHeight > 0) {
94
- customViewHeight
93
+ val height = if (contentHeight > 0) {
94
+ contentHeight
95
95
  } else if (selfParams != null && selfParams.height > 0) {
96
96
  selfParams.height
97
97
  } else {
@@ -123,12 +123,12 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
123
123
  val fallbackHeightSize = resolveExplicitMeasureSize(parentHeightSize, false)
124
124
 
125
125
  val contentWidthSpec = when {
126
- customViewWidth > 0 -> MeasureSpec.makeMeasureSpec(customViewWidth, MeasureSpec.EXACTLY)
126
+ contentWidth > 0 -> MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY)
127
127
  fallbackWidthSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackWidthSize, MeasureSpec.AT_MOST)
128
128
  else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
129
129
  }
130
130
  val contentHeightSpec = when {
131
- customViewHeight > 0 -> MeasureSpec.makeMeasureSpec(customViewHeight, MeasureSpec.EXACTLY)
131
+ contentHeight > 0 -> MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY)
132
132
  fallbackHeightSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackHeightSize, MeasureSpec.AT_MOST)
133
133
  else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
134
134
  }
@@ -158,13 +158,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
158
158
  measuredContentHeight = max(measuredContentHeight, childBounds?.height() ?: child.measuredHeight)
159
159
  }
160
160
 
161
- val desiredWidth = if (customViewWidth > 0) {
162
- customViewWidth
161
+ val desiredWidth = if (contentWidth > 0) {
162
+ contentWidth
163
163
  } else {
164
164
  measuredContentWidth + paddingLeft + paddingRight
165
165
  }
166
- val desiredHeight = if (customViewHeight > 0) {
167
- customViewHeight
166
+ val desiredHeight = if (contentHeight > 0) {
167
+ contentHeight
168
168
  } else {
169
169
  measuredContentHeight + paddingTop + paddingBottom
170
170
  }
@@ -218,8 +218,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
218
218
  private var pendingLongitude: Double? = null // 临时存储经度
219
219
  private var iconWidth: Int = 0 // 用于自定义图标的宽度
220
220
  private var iconHeight: Int = 0 // 用于自定义图标的高度
221
- private var customViewWidth: Int = 0 // 用于自定义视图(children)的宽度
222
- private var customViewHeight: Int = 0 // 用于自定义视图(children)的高度
221
+ private var contentWidth: Int = 0 // 用于自定义视图(children)的宽度
222
+ private var contentHeight: Int = 0 // 用于自定义视图(children)的高度
223
223
  private val mainHandler = Handler(Looper.getMainLooper())
224
224
  private var isRemoving = false // 标记是否正在被移除
225
225
  private var pendingMarkerIconUpdate: Runnable? = null
@@ -479,7 +479,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
479
479
  return
480
480
  }
481
481
 
482
- invalidateAppliedCustomMarkerCaches()
482
+ invalidateAppliedCustomMarkerCaches(clearGlobalCache = true)
483
483
  cacheKey = key
484
484
  updateMarkerIcon()
485
485
  }
@@ -738,32 +738,32 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
738
738
  }
739
739
 
740
740
  /**
741
- * 设置自定义视图宽度(用于 children 属性)
741
+ * 设置内容宽度(用于 children 属性)
742
742
  * 注意:React Native 传入的是 DP 值,需要转换为 PX
743
743
  */
744
- fun setCustomViewWidth(width: Int) {
744
+ fun setContentWidth(width: Int) {
745
745
  val density = context.resources.displayMetrics.density
746
746
  val resolvedWidth = (width * density).toInt()
747
- if (customViewWidth == resolvedWidth) {
747
+ if (contentWidth == resolvedWidth) {
748
748
  return
749
749
  }
750
750
 
751
- customViewWidth = resolvedWidth
751
+ contentWidth = resolvedWidth
752
752
  markCustomMarkerContentDirty()
753
753
  }
754
754
 
755
755
  /**
756
- * 设置自定义视图高度(用于 children 属性)
756
+ * 设置内容高度(用于 children 属性)
757
757
  * 注意:React Native 传入的是 DP 值,需要转换为 PX
758
758
  */
759
- fun setCustomViewHeight(height: Int) {
759
+ fun setContentHeight(height: Int) {
760
760
  val density = context.resources.displayMetrics.density
761
761
  val resolvedHeight = (height * density).toInt()
762
- if (customViewHeight == resolvedHeight) {
762
+ if (contentHeight == resolvedHeight) {
763
763
  return
764
764
  }
765
765
 
766
- customViewHeight = resolvedHeight
766
+ contentHeight = resolvedHeight
767
767
  markCustomMarkerContentDirty()
768
768
  }
769
769
 
@@ -904,8 +904,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
904
904
  return MarkerBitmapRenderer.createBitmap(
905
905
  container = this,
906
906
  snapshot = resolvedSnapshot,
907
- customViewWidth = customViewWidth,
908
- customViewHeight = customViewHeight,
907
+ contentWidth = contentWidth,
908
+ contentHeight = contentHeight,
909
909
  mainHandler = mainHandler,
910
910
  )
911
911
  }
@@ -997,10 +997,12 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
997
997
  }
998
998
  }
999
999
 
1000
- private fun invalidateAppliedCustomMarkerCaches() {
1000
+ private fun invalidateAppliedCustomMarkerCaches(clearGlobalCache: Boolean = false) {
1001
1001
  val key = lastAppliedCustomMarkerKey ?: return
1002
- BitmapDescriptorCache.remove(key)
1003
- IconBitmapCache.remove(key)
1002
+ if (clearGlobalCache) {
1003
+ BitmapDescriptorCache.remove(key)
1004
+ IconBitmapCache.remove(key)
1005
+ }
1004
1006
  lastAppliedCustomMarkerKey = null
1005
1007
  }
1006
1008
 
@@ -1009,7 +1011,10 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1009
1011
  return
1010
1012
  }
1011
1013
 
1012
- invalidateAppliedCustomMarkerCaches()
1014
+ // children(尤其是 Image)异步加载完成后,需要强制淘汰当前 marker 已应用的位图缓存,
1015
+ // 否则同一个 cacheKey 会一直命中“占位态”的旧 bitmap,表现为灰块/蒙层不更新。
1016
+ // 这里仅清理当前 marker 已应用的 key,不会影响其它 marker。
1017
+ invalidateAppliedCustomMarkerCaches(clearGlobalCache = true)
1013
1018
  if (marker != null) {
1014
1019
  scheduleMarkerIconUpdate(delayMs)
1015
1020
  }
@@ -1048,8 +1053,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1048
1053
  private fun resolveMarkerBitmapSnapshot(): MarkerBitmapSnapshot? =
1049
1054
  MarkerBitmapRenderer.resolveSnapshot(
1050
1055
  container = this,
1051
- customViewWidth = customViewWidth,
1052
- customViewHeight = customViewHeight,
1056
+ contentWidth = contentWidth,
1057
+ contentHeight = contentHeight,
1053
1058
  cacheKey = cacheKey,
1054
1059
  )
1055
1060
 
@@ -1059,13 +1064,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1059
1064
  val childCountBefore = childCount
1060
1065
 
1061
1066
  val sourceWidth = when {
1062
- customViewWidth > 0 -> customViewWidth
1067
+ contentWidth > 0 -> contentWidth
1063
1068
  params?.width != null && params.width > 0 -> params.width
1064
1069
  else -> LayoutParams.WRAP_CONTENT
1065
1070
  }
1066
1071
 
1067
1072
  val sourceHeight = when {
1068
- customViewHeight > 0 -> customViewHeight
1073
+ contentHeight > 0 -> contentHeight
1069
1074
  params?.height != null && params.height > 0 -> params.height
1070
1075
  else -> LayoutParams.WRAP_CONTENT
1071
1076
  }
@@ -69,13 +69,13 @@ class MarkerViewModule : Module() {
69
69
  Prop<Int>("iconHeight") { view, height ->
70
70
  view.setIconHeight(height)
71
71
  }
72
- // 自定义视图宽度
73
- Prop<Int>("customViewWidth") { view, width ->
74
- view.setCustomViewWidth(width)
72
+ // 内容宽度
73
+ Prop<Int>("contentWidth") { view, width ->
74
+ view.setContentWidth(width)
75
75
  }
76
- // 自定义视图高度
77
- Prop<Int>("customViewHeight") { view, height ->
78
- view.setCustomViewHeight(height)
76
+ // 内容高度
77
+ Prop<Int>("contentHeight") { view, height ->
78
+ view.setContentHeight(height)
79
79
  }
80
80
  // 缓存key
81
81
  Prop<String?>("cacheKey") { view, key ->