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
@@ -4,20 +4,16 @@ import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.graphics.Bitmap
6
6
  import android.graphics.BitmapFactory
7
- import android.graphics.Canvas
8
- import android.graphics.Color
9
7
  import android.graphics.Rect
10
-
11
8
  import android.os.Handler
12
9
  import android.os.Looper
13
10
  import android.view.View
11
+ import android.view.ViewParent
14
12
  import com.amap.api.maps.AMap
15
-
16
13
  import com.amap.api.maps.model.BitmapDescriptorFactory
17
14
  import com.amap.api.maps.model.LatLng
18
15
  import com.amap.api.maps.model.Marker
19
16
  import com.amap.api.maps.model.MarkerOptions
20
-
21
17
  import com.amap.api.maps.utils.SpatialRelationUtil
22
18
  import com.amap.api.maps.utils.overlay.MovingPointOverlay
23
19
  import expo.modules.kotlin.AppContext
@@ -33,8 +29,6 @@ import androidx.core.view.contains
33
29
  import androidx.core.view.isEmpty
34
30
  import androidx.core.graphics.scale
35
31
  import android.view.ViewGroup
36
- import android.widget.ImageView
37
- import android.widget.TextView
38
32
  import com.amap.api.maps.model.animation.AlphaAnimation
39
33
  import com.amap.api.maps.model.animation.AnimationSet
40
34
  import com.amap.api.maps.model.animation.ScaleAnimation
@@ -42,13 +36,8 @@ import android.view.animation.DecelerateInterpolator
42
36
  import expo.modules.gaodemap.companion.BitmapDescriptorCache
43
37
  import expo.modules.gaodemap.companion.IconBitmapCache
44
38
  import expo.modules.gaodemap.utils.GeometryUtils
45
- import kotlin.text.StringBuilder
46
39
  import kotlin.math.max
47
40
  import kotlin.math.min
48
-
49
- import java.util.concurrent.CountDownLatch
50
- import java.util.concurrent.TimeUnit
51
- import androidx.core.graphics.createBitmap
52
41
  import expo.modules.gaodemap.utils.LatLngParser
53
42
 
54
43
  class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
@@ -164,7 +153,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
164
153
  )
165
154
 
166
155
  child.measure(childWidthSpec, childHeightSpec)
167
- val childBounds = computeContentBounds(child)
156
+ val childBounds = MarkerBitmapRenderer.computeContentBounds(child)
168
157
  measuredContentWidth = max(measuredContentWidth, childBounds?.width() ?: child.measuredWidth)
169
158
  measuredContentHeight = max(measuredContentHeight, childBounds?.height() ?: child.measuredHeight)
170
159
  }
@@ -486,6 +475,11 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
486
475
  * JS 端传入稳定的缓存 key
487
476
  */
488
477
  fun setCacheKey(key: String?) {
478
+ if (cacheKey == key) {
479
+ return
480
+ }
481
+
482
+ invalidateAppliedCustomMarkerCaches()
489
483
  cacheKey = key
490
484
  updateMarkerIcon()
491
485
  }
@@ -715,7 +709,15 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
715
709
  */
716
710
  fun setIconWidth(width: Int) {
717
711
  val density = context.resources.displayMetrics.density
718
- iconWidth = (width * density).toInt()
712
+ val resolvedWidth = (width * density).toInt()
713
+ if (iconWidth == resolvedWidth) {
714
+ return
715
+ }
716
+
717
+ iconWidth = resolvedWidth
718
+ pendingIconUri?.let { iconUri ->
719
+ marker?.let { loadAndSetIcon(iconUri, it) }
720
+ }
719
721
  }
720
722
 
721
723
  /**
@@ -724,7 +726,15 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
724
726
  */
725
727
  fun setIconHeight(height: Int) {
726
728
  val density = context.resources.displayMetrics.density
727
- iconHeight = (height * density).toInt()
729
+ val resolvedHeight = (height * density).toInt()
730
+ if (iconHeight == resolvedHeight) {
731
+ return
732
+ }
733
+
734
+ iconHeight = resolvedHeight
735
+ pendingIconUri?.let { iconUri ->
736
+ marker?.let { loadAndSetIcon(iconUri, it) }
737
+ }
728
738
  }
729
739
 
730
740
  /**
@@ -733,7 +743,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
733
743
  */
734
744
  fun setCustomViewWidth(width: Int) {
735
745
  val density = context.resources.displayMetrics.density
736
- customViewWidth = (width * density).toInt()
746
+ val resolvedWidth = (width * density).toInt()
747
+ if (customViewWidth == resolvedWidth) {
748
+ return
749
+ }
750
+
751
+ customViewWidth = resolvedWidth
752
+ markCustomMarkerContentDirty()
737
753
  }
738
754
 
739
755
  /**
@@ -742,7 +758,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
742
758
  */
743
759
  fun setCustomViewHeight(height: Int) {
744
760
  val density = context.resources.displayMetrics.density
745
- customViewHeight = (height * density).toInt()
761
+ val resolvedHeight = (height * density).toInt()
762
+ if (customViewHeight == resolvedHeight) {
763
+ return
764
+ }
765
+
766
+ customViewHeight = resolvedHeight
767
+ markCustomMarkerContentDirty()
746
768
  }
747
769
 
748
770
  /**
@@ -769,7 +791,6 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
769
791
  // 只有在没有自定义内容(children)且有 title 或 snippet 时才显示信息窗口
770
792
  // 如果有自定义内容,说明用户已经自定义了显示内容,不需要默认信息窗口
771
793
  return !(view.isEmpty() && (!marker.title.isNullOrEmpty() || !marker.snippet.isNullOrEmpty()))
772
- // marker.showInfoWindow()
773
794
  }
774
795
  return false
775
796
  }
@@ -876,176 +897,17 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
876
897
  *
877
898
  * 注意:render 会在 UI 线程执行;如果当前线程不是 UI 线程,会同步等待 UI 线程完成(有超时)。
878
899
  */
879
- private fun createBitmapFromView(): Bitmap? {
900
+ private fun createBitmapFromView(snapshot: MarkerBitmapSnapshot? = resolveMarkerBitmapSnapshot()): Bitmap? {
880
901
  if (isEmpty()) return null
881
902
 
882
- // 优先使用 JS 传入的 cacheKey,如果没有则 fallback 为 fingerprint
883
- val keyPart = cacheKey ?: computeViewFingerprint(this)
884
-
885
- val measuredChild = if (isNotEmpty()) getChildAt(0) else null
886
- val contentView = resolveRenderableContentView(measuredChild)
887
- val contentBounds = computeContentBounds(measuredChild)
888
- val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: measuredChild?.measuredWidth ?: 0
889
- val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: measuredChild?.measuredHeight ?: 0
890
-
891
- val finalWidth = if (measuredWidth > 0) measuredWidth else (if (customViewWidth > 0) customViewWidth else 0)
892
- val finalHeight = if (measuredHeight > 0) measuredHeight else (if (customViewHeight > 0) customViewHeight else 0)
893
-
894
- // 🔑 修复:如果尺寸为 0,说明 View 还没准备好,不要生成 Bitmap,否则会导致动画位置偏移
895
- if (finalWidth <= 0 || finalHeight <= 0) {
896
- return null
897
- }
898
-
899
- val fullCacheKey = "$keyPart|${finalWidth}x${finalHeight}"
900
-
901
- // 1) 尝试缓存命中
902
- IconBitmapCache.get(fullCacheKey)?.let { return it }
903
-
904
- // 2) 未命中,则生成 bitmap(同之前逻辑)
905
- val bitmap: Bitmap? = if (Looper.myLooper() == Looper.getMainLooper()) {
906
- renderViewToBitmapInternal(finalWidth, finalHeight)
907
- } else {
908
- val latch = CountDownLatch(1)
909
- var result: Bitmap? = null
910
- mainHandler.post {
911
- try {
912
- result = renderViewToBitmapInternal(finalWidth, finalHeight)
913
- } finally {
914
- latch.countDown()
915
- }
916
- }
917
- try { latch.await(200, TimeUnit.MILLISECONDS) } catch (_: InterruptedException) {}
918
- result
919
- }
920
-
921
- bitmap?.let { IconBitmapCache.put(fullCacheKey, it) }
922
- return bitmap
923
- }
924
-
925
-
926
- /**
927
- * 真正把 view measure/layout/draw 到 Bitmap 的内部方法(必须在主线程调用)
928
- */
929
- private fun renderViewToBitmapInternal(finalWidth: Int, finalHeight: Int): Bitmap? {
930
- try {
931
- val childView = if (isNotEmpty()) getChildAt(0) else return null
932
-
933
-
934
- // 🔑 优化:如果 View 尺寸已经符合要求,直接复用现有布局,避免破坏 React Native 的排版
935
- if (childView.width != finalWidth || childView.height != finalHeight) {
936
- // 🔑 关键修复:如果子 View 还没完成布局(宽高为 0),不要强行 measure,这会导致布局错乱(如 0x0 -> 252x75)。
937
- // 直接返回 null,等待下一次 layout(当子 View 准备好时会再次触发)。
938
- if (childView.width == 0 || childView.height == 0) {
939
- return null
940
- }
941
-
942
- // 使用给定的尺寸强制测量布局
943
- val widthSpec = MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY)
944
- val heightSpec = MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)
945
-
946
- // measure + layout
947
- childView.measure(widthSpec, heightSpec)
948
- childView.layout(0, 0, finalWidth, finalHeight)
949
- } else {
950
- // 如果复用布局,必须检查 left/top 是否为 0。如果不为 0,绘制到 bitmap 时会发生偏移。
951
- // 很多时候 RN 会给 view 设置 left/top。
952
- if (childView.left != 0 || childView.top != 0) {
953
- childView.layout(0, 0, finalWidth, finalHeight)
954
- }
955
- }
956
-
957
- // 🔑 修复:创建支持透明度的 bitmap 配置
958
- val bitmap = createBitmap(finalWidth, finalHeight)
959
- val canvas = Canvas(bitmap)
960
-
961
- // 🔑 关键修复:强制启用 view 的绘制缓存,确保内容正确渲染
962
- childView.isDrawingCacheEnabled = true
963
- childView.buildDrawingCache(true)
964
-
965
- // 绘制 view 到 canvas
966
- childView.draw(canvas)
967
-
968
- // 清理绘制缓存
969
- childView.isDrawingCacheEnabled = false
970
- childView.destroyDrawingCache()
971
-
972
- val shouldTrimTransparentPadding = customViewWidth <= 0 && customViewHeight <= 0
973
- return if (shouldTrimTransparentPadding) trimTransparentPadding(bitmap) else bitmap
974
- } catch (_: Exception) {
975
- // 遇到异常时返回 null,让上层使用默认图标
976
- return null
977
- }
978
- }
979
-
980
- private fun trimTransparentPadding(bitmap: Bitmap): Bitmap {
981
- if (bitmap.width <= 1 || bitmap.height <= 1) {
982
- return bitmap
983
- }
984
-
985
- if (hasOpaquePixelsOnAllBitmapEdges(bitmap)) {
986
- return bitmap
987
- }
988
-
989
- var minX = bitmap.width
990
- var minY = bitmap.height
991
- var maxX = -1
992
- var maxY = -1
993
-
994
- for (y in 0 until bitmap.height) {
995
- for (x in 0 until bitmap.width) {
996
- if (Color.alpha(bitmap.getPixel(x, y)) != 0) {
997
- if (x < minX) minX = x
998
- if (y < minY) minY = y
999
- if (x > maxX) maxX = x
1000
- if (y > maxY) maxY = y
1001
- }
1002
- }
1003
- }
1004
-
1005
- if (maxX < minX || maxY < minY) {
1006
- return bitmap
1007
- }
1008
-
1009
- val trimmedWidth = maxX - minX + 1
1010
- val trimmedHeight = maxY - minY + 1
1011
- if (trimmedWidth == bitmap.width && trimmedHeight == bitmap.height) {
1012
- return bitmap
1013
- }
1014
-
1015
- return Bitmap.createBitmap(bitmap, minX, minY, trimmedWidth, trimmedHeight)
1016
- }
1017
-
1018
- private fun hasOpaquePixelsOnAllBitmapEdges(bitmap: Bitmap): Boolean {
1019
- var topEdgeHasPixel = false
1020
- var bottomEdgeHasPixel = false
1021
- var leftEdgeHasPixel = false
1022
- var rightEdgeHasPixel = false
1023
-
1024
- for (x in 0 until bitmap.width) {
1025
- if (!topEdgeHasPixel && Color.alpha(bitmap.getPixel(x, 0)) != 0) {
1026
- topEdgeHasPixel = true
1027
- }
1028
- if (!bottomEdgeHasPixel && Color.alpha(bitmap.getPixel(x, bitmap.height - 1)) != 0) {
1029
- bottomEdgeHasPixel = true
1030
- }
1031
- if (topEdgeHasPixel && bottomEdgeHasPixel) {
1032
- break
1033
- }
1034
- }
1035
-
1036
- for (y in 0 until bitmap.height) {
1037
- if (!leftEdgeHasPixel && Color.alpha(bitmap.getPixel(0, y)) != 0) {
1038
- leftEdgeHasPixel = true
1039
- }
1040
- if (!rightEdgeHasPixel && Color.alpha(bitmap.getPixel(bitmap.width - 1, y)) != 0) {
1041
- rightEdgeHasPixel = true
1042
- }
1043
- if (leftEdgeHasPixel && rightEdgeHasPixel) {
1044
- break
1045
- }
1046
- }
1047
-
1048
- return topEdgeHasPixel && bottomEdgeHasPixel && leftEdgeHasPixel && rightEdgeHasPixel
903
+ val resolvedSnapshot = snapshot ?: return null
904
+ return MarkerBitmapRenderer.createBitmap(
905
+ container = this,
906
+ snapshot = resolvedSnapshot,
907
+ customViewWidth = customViewWidth,
908
+ customViewHeight = customViewHeight,
909
+ mainHandler = mainHandler,
910
+ )
1049
911
  }
1050
912
 
1051
913
  /**
@@ -1064,14 +926,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1064
926
  return
1065
927
  }
1066
928
 
1067
- // 构建缓存 key(优先 JS cacheKey)
1068
- val keyPart = cacheKey ?: computeViewFingerprint(this)
1069
- val child = getChildAt(0)
1070
- val contentView = resolveRenderableContentView(child)
1071
- val contentBounds = computeContentBounds(child)
1072
- val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: child?.measuredWidth ?: customViewWidth
1073
- val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: child?.measuredHeight ?: customViewHeight
1074
- val fullCacheKey = "$keyPart|${measuredWidth}x${measuredHeight}"
929
+ val snapshot = resolveMarkerBitmapSnapshot() ?: run {
930
+ if (marker?.isVisible != true) {
931
+ // 自定义 view 还没准备好时,继续等待下一次 layout/update。
932
+ }
933
+ return
934
+ }
935
+ val fullCacheKey = snapshot.fullCacheKey
1075
936
 
1076
937
  // 确定锚点:优先使用用户指定的 pendingAnchor,否则对于自定义 View 使用中心点 (0.5, 0.5)
1077
938
  val anchorX = pendingAnchor?.first ?: 0.5f
@@ -1093,7 +954,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1093
954
  }
1094
955
 
1095
956
  // 2) Bitmap 缓存命中则生成 Descriptor,或者重新生成
1096
- val bitmap = IconBitmapCache.get(fullCacheKey) ?: createBitmapFromView() ?: run {
957
+ val bitmap = IconBitmapCache.get(fullCacheKey) ?: createBitmapFromView(snapshot) ?: run {
1097
958
  // 🔑 关键修复:如果生成 Bitmap 失败(例如 View 还没准备好)
1098
959
  // 不要急着切回默认 Marker,这会导致闪烁和位置跳变。
1099
960
  // 只有在 Marker 从未显示过的情况下,才考虑兜底策略。
@@ -1136,6 +997,24 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1136
997
  }
1137
998
  }
1138
999
 
1000
+ private fun invalidateAppliedCustomMarkerCaches() {
1001
+ val key = lastAppliedCustomMarkerKey ?: return
1002
+ BitmapDescriptorCache.remove(key)
1003
+ IconBitmapCache.remove(key)
1004
+ lastAppliedCustomMarkerKey = null
1005
+ }
1006
+
1007
+ private fun markCustomMarkerContentDirty(delayMs: Long = 16L) {
1008
+ if (isRemoving || isEmpty()) {
1009
+ return
1010
+ }
1011
+
1012
+ invalidateAppliedCustomMarkerCaches()
1013
+ if (marker != null) {
1014
+ scheduleMarkerIconUpdate(delayMs)
1015
+ }
1016
+ }
1017
+
1139
1018
 
1140
1019
 
1141
1020
  override fun removeView(child: View?) {
@@ -1156,8 +1035,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1156
1035
  if (index in 0..<childCount) {
1157
1036
  super.removeViewAt(index)
1158
1037
  // 只在还有子视图时更新图标
1159
- if (!isRemoving && childCount > 1 && marker != null) {
1160
- scheduleMarkerIconUpdate(50)
1038
+ if (!isRemoving && childCount > 0 && marker != null) {
1039
+ markCustomMarkerContentDirty(50)
1161
1040
  }
1162
1041
  // 如果最后一个子视图被移除,什么都不做
1163
1042
  // 让 onDetachedFromWindow 处理完整的清理
@@ -1166,59 +1045,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1166
1045
  // 忽略异常
1167
1046
  }
1168
1047
  }
1169
- private fun resolveRenderableContentView(view: View?): View? {
1170
- var current = view ?: return null
1171
-
1172
- while (current is ViewGroup && current.childCount == 1) {
1173
- val next = current.getChildAt(0) ?: break
1174
- current = next
1175
- }
1176
-
1177
- return current
1178
- }
1179
-
1180
- private fun computeContentBounds(view: View?): Rect? {
1181
- view ?: return null
1182
- if (view.visibility != View.VISIBLE) return null
1183
-
1184
- var resolvedBounds: Rect? = null
1185
-
1186
- if (view is ViewGroup && view.childCount > 0) {
1187
- for (i in 0 until view.childCount) {
1188
- val child = view.getChildAt(i) ?: continue
1189
- val childBounds = computeContentBounds(child) ?: continue
1190
- val shiftedBounds = Rect(childBounds)
1191
- shiftedBounds.offset(child.left, child.top)
1192
- resolvedBounds = if (resolvedBounds == null) {
1193
- shiftedBounds
1194
- } else {
1195
- Rect(resolvedBounds).apply { union(shiftedBounds) }
1196
- }
1197
- }
1198
- }
1199
-
1200
- val hasOwnVisualBounds =
1201
- view.background != null ||
1202
- view.paddingLeft != 0 ||
1203
- view.paddingTop != 0 ||
1204
- view.paddingRight != 0 ||
1205
- view.paddingBottom != 0 ||
1206
- view !is ViewGroup
1207
-
1208
- val ownWidth = view.measuredWidth.takeIf { it > 0 } ?: view.width
1209
- val ownHeight = view.measuredHeight.takeIf { it > 0 } ?: view.height
1210
-
1211
- if (hasOwnVisualBounds && ownWidth > 0 && ownHeight > 0) {
1212
- val ownBounds = Rect(0, 0, ownWidth, ownHeight)
1213
- resolvedBounds = if (resolvedBounds == null) {
1214
- ownBounds
1215
- } else {
1216
- Rect(resolvedBounds).apply { union(ownBounds) }
1217
- }
1218
- }
1219
-
1220
- return resolvedBounds
1221
- }
1048
+ private fun resolveMarkerBitmapSnapshot(): MarkerBitmapSnapshot? =
1049
+ MarkerBitmapRenderer.resolveSnapshot(
1050
+ container = this,
1051
+ customViewWidth = customViewWidth,
1052
+ customViewHeight = customViewHeight,
1053
+ cacheKey = cacheKey,
1054
+ )
1222
1055
 
1223
1056
 
1224
1057
  override fun addView(child: View?, index: Int, params: android.view.ViewGroup.LayoutParams?) {
@@ -1257,7 +1090,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1257
1090
  // 🔑 修复:需要延迟更新图标,等待 children 完成布局
1258
1091
  // 原因:立即更新会在 children 还未完成测量/布局时就渲染,导致内容为空
1259
1092
  if (!isRemoving && marker != null && childCount > childCountBefore) {
1260
- scheduleMarkerIconUpdate()
1093
+ markCustomMarkerContentDirty()
1261
1094
  }
1262
1095
  }
1263
1096
 
@@ -1266,10 +1099,19 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1266
1099
  // 🔑 修复:布局完成后延迟更新图标
1267
1100
  // 即使 changed 为 false,只要有内容,也应该检查是否需要更新(例如子 View 尺寸变化但 MarkerView 没变)
1268
1101
  if (!isRemoving && isNotEmpty() && marker != null) {
1269
- scheduleMarkerIconUpdate()
1102
+ markCustomMarkerContentDirty()
1270
1103
  }
1271
1104
  }
1272
1105
 
1106
+ @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
1107
+ override fun invalidateChildInParent(location: IntArray?, dirty: Rect?): ViewParent? {
1108
+ val parentRef = super.invalidateChildInParent(location, dirty)
1109
+ if (!isRemoving && isNotEmpty() && marker != null) {
1110
+ markCustomMarkerContentDirty()
1111
+ }
1112
+ return parentRef
1113
+ }
1114
+
1273
1115
 
1274
1116
 
1275
1117
  /**
@@ -1517,62 +1359,4 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1517
1359
  }
1518
1360
  }
1519
1361
 
1520
-
1521
- /**
1522
- * 为 view 和其子树生成一个轻量“指纹”字符串,用作缓存 key。
1523
- * 注意:这是启发式的,不追求 100% 唯一性,但在大部分自定义 view 场景下能稳定复用。
1524
- */
1525
- fun computeViewFingerprint(view: View?): String {
1526
- if (view == null) return "null"
1527
-
1528
- val sb = StringBuilder()
1529
- // 首先尝试使用开发者可能预设的 tag 或 contentDescription 作为优先标识(稳定且快速)
1530
- val tag = view.tag
1531
- if (tag != null) {
1532
- sb.append("tag=").append(tag.toString()).append(";")
1533
- return sb.toString()
1534
- }
1535
-
1536
- val contentDesc = view.contentDescription
1537
- if (!contentDesc.isNullOrEmpty()) {
1538
- sb.append("cdesc=").append(contentDesc.toString()).append(";")
1539
- return sb.toString()
1540
- }
1541
-
1542
- // 否则做一个递归采样:className + 对于 TextView 获取 text + 对于 ImageView 获取 resourceId 或 drawable hash
1543
- fun appendFor(v: View) {
1544
- sb.append(v.javaClass.simpleName)
1545
- when (v) {
1546
- is TextView -> {
1547
- val t = v.text?.toString() ?: ""
1548
- if (t.isNotEmpty()) {
1549
- sb.append("[text=").append(t).append("]")
1550
- }
1551
- }
1552
- is ImageView -> {
1553
- // 尝试读取资源 id(若使用 setImageResource 时可取到),否则取 drawable 的 hashCode 作为近似
1554
- val resId = v.tag // 开发者可将资源 id 放到 tag 以便稳定识别
1555
- if (resId is Int && resId != 0) {
1556
- sb.append("[imgRes=").append(resId).append("]")
1557
- } else {
1558
- val dr = v.drawable
1559
- if (dr != null) {
1560
- sb.append("[drawableHash=").append(dr.hashCode()).append("]")
1561
- }
1562
- }
1563
- }
1564
- }
1565
- sb.append(";")
1566
- if (v is ViewGroup) {
1567
- for (i in 0 until v.childCount) {
1568
- val c = v.getChildAt(i)
1569
- appendFor(c)
1570
- }
1571
- }
1572
- }
1573
-
1574
- appendFor(view)
1575
- // 最终返回一个截断的 sha-like 形式(避免 key 过长)
1576
- return sb.toString().take(1024)
1577
- }
1578
1362
  }
@@ -77,9 +77,9 @@ class MarkerViewModule : Module() {
77
77
  Prop<Int>("customViewHeight") { view, height ->
78
78
  view.setCustomViewHeight(height)
79
79
  }
80
- // 缓存key
81
- Prop<String>("cacheKey") { view, key ->
82
- view.setCacheKey(key)
80
+ // 缓存key
81
+ Prop<String?>("cacheKey") { view, key ->
82
+ view.setCacheKey(key)
83
83
  }
84
84
 
85
85
  // 平滑移动路径
@@ -1,4 +1,4 @@
1
- import { LatLng, Coordinates, ReGeocode, LocationListener, LatLngPoint, CoordinateType } from './types';
1
+ import { LatLng, Coordinates, ReGeocode, LocationListener, HeadingListener, LatLngPoint, CoordinateType, GeoLanguage } from './types';
2
2
  import type { ExpoGaodeMapModule as NativeExpoGaodeMapModule } from './types/native-module.types';
3
3
  import { PrivacyConfig, PrivacyStatus, SDKConfig, PermissionStatus } from './types/common.types';
4
4
  declare const helperMethods: {
@@ -42,6 +42,7 @@ declare const helperMethods: {
42
42
  isStarted(): Promise<boolean>;
43
43
  getCurrentLocation(): Promise<Coordinates | ReGeocode>;
44
44
  coordinateConvert(coordinate: LatLngPoint, type: CoordinateType): Promise<LatLng>;
45
+ setGeoLanguage(language: GeoLanguage | string): void;
45
46
  setLocatingWithReGeocode(isReGeocode: boolean): void;
46
47
  readonly isBackgroundLocationEnabled: boolean;
47
48
  /**
@@ -73,6 +74,13 @@ declare const helperMethods: {
73
74
  addLocationListener(listener: LocationListener): {
74
75
  remove: () => void;
75
76
  };
77
+ /**
78
+ * 添加方向监听器(iOS)
79
+ * 自动归一化 heading 事件字段,兼容旧版原生返回结构
80
+ */
81
+ addHeadingListener(listener: HeadingListener): {
82
+ remove: () => void;
83
+ };
76
84
  /**
77
85
  * 计算两个坐标点之间的距离
78
86
  * @param coordinate1 第一个坐标点
@@ -91,16 +99,16 @@ declare const helperMethods: {
91
99
  /**
92
100
  * 判断点是否在多边形内
93
101
  * @param point 要判断的点
94
- * @param polygon 多边形的顶点坐标数组
102
+ * @param polygon 多边形的顶点坐标数组,支持嵌套数组(多边形空洞)
95
103
  * @returns 是否在多边形内
96
104
  */
97
- isPointInPolygon(point: LatLngPoint, polygon: LatLngPoint[]): boolean;
105
+ isPointInPolygon(point: LatLngPoint, polygon: LatLngPoint[] | LatLngPoint[][]): boolean;
98
106
  /**
99
107
  * 计算多边形面积
100
- * @param polygon 多边形的顶点坐标数组
108
+ * @param polygon 多边形的顶点坐标数组,支持嵌套数组(多边形空洞)
101
109
  * @returns 面积(单位:平方米)
102
110
  */
103
- calculatePolygonArea(polygon: LatLngPoint[]): number;
111
+ calculatePolygonArea(polygon: LatLngPoint[] | LatLngPoint[][]): number;
104
112
  /**
105
113
  * 计算矩形面积
106
114
  * @param southWest 西南角坐标
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"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"}