expo-gaode-map-navigation 2.0.5 → 2.0.6

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 (55) hide show
  1. package/README.md +52 -1
  2. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +182 -86
  3. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +5 -2
  4. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +19 -5
  5. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +319 -48
  6. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +3 -3
  7. package/build/index.d.ts +8 -4
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +79 -1
  10. package/build/index.js.map +1 -1
  11. package/build/map/ExpoGaodeMapModule.d.ts +4 -4
  12. package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
  13. package/build/map/ExpoGaodeMapModule.js +10 -8
  14. package/build/map/ExpoGaodeMapModule.js.map +1 -1
  15. package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
  16. package/build/map/ExpoGaodeMapView.js +79 -17
  17. package/build/map/ExpoGaodeMapView.js.map +1 -1
  18. package/build/map/components/overlays/Cluster.d.ts.map +1 -1
  19. package/build/map/components/overlays/Cluster.js +12 -0
  20. package/build/map/components/overlays/Cluster.js.map +1 -1
  21. package/build/map/components/overlays/Marker.d.ts.map +1 -1
  22. package/build/map/components/overlays/Marker.js +70 -6
  23. package/build/map/components/overlays/Marker.js.map +1 -1
  24. package/build/map/types/common.types.d.ts +29 -5
  25. package/build/map/types/common.types.d.ts.map +1 -1
  26. package/build/map/types/common.types.js +5 -5
  27. package/build/map/types/common.types.js.map +1 -1
  28. package/build/map/types/index.d.ts +2 -1
  29. package/build/map/types/index.d.ts.map +1 -1
  30. package/build/map/types/index.js.map +1 -1
  31. package/build/map/types/location.types.d.ts +23 -0
  32. package/build/map/types/location.types.d.ts.map +1 -1
  33. package/build/map/types/location.types.js.map +1 -1
  34. package/build/map/types/map-view.types.d.ts +20 -22
  35. package/build/map/types/map-view.types.d.ts.map +1 -1
  36. package/build/map/types/map-view.types.js.map +1 -1
  37. package/build/map/types/overlays.types.d.ts +9 -2
  38. package/build/map/types/overlays.types.d.ts.map +1 -1
  39. package/build/map/types/overlays.types.js.map +1 -1
  40. package/build/map/types/route-playback.types.d.ts +12 -0
  41. package/build/map/types/route-playback.types.d.ts.map +1 -0
  42. package/build/map/types/route-playback.types.js +2 -0
  43. package/build/map/types/route-playback.types.js.map +1 -0
  44. package/build/types/route.types.d.ts +10 -1
  45. package/build/types/route.types.d.ts.map +1 -1
  46. package/build/types/route.types.js +2 -0
  47. package/build/types/route.types.js.map +1 -1
  48. package/ios/map/ExpoGaodeMapView.swift +151 -76
  49. package/ios/map/ExpoGaodeMapViewModule.swift +14 -1
  50. package/ios/map/managers/UIManager.swift +5 -4
  51. package/ios/map/overlays/ClusterView.swift +207 -147
  52. package/ios/map/overlays/ClusterViewModule.swift +5 -1
  53. package/ios/map/overlays/MarkerView.swift +214 -60
  54. package/ios/map/overlays/MarkerViewModule.swift +1 -1
  55. package/package.json +1 -1
@@ -5,10 +5,19 @@ import android.content.Context
5
5
  import android.graphics.Bitmap
6
6
  import android.graphics.BitmapFactory
7
7
  import android.graphics.Canvas
8
-
8
+ import android.graphics.Color
9
+ import android.graphics.Typeface
10
+ import android.graphics.Rect
11
+ import android.graphics.drawable.GradientDrawable
12
+ import android.graphics.drawable.LayerDrawable
9
13
  import android.os.Handler
10
14
  import android.os.Looper
15
+ import android.util.TypedValue
16
+ import android.view.Gravity
11
17
  import android.view.View
18
+ import android.text.TextUtils
19
+ import android.widget.LinearLayout
20
+ import android.widget.TextView
12
21
  import com.amap.api.maps.AMap
13
22
 
14
23
  import com.amap.api.maps.model.BitmapDescriptorFactory
@@ -41,6 +50,8 @@ import expo.modules.gaodemap.map.companion.BitmapDescriptorCache
41
50
  import expo.modules.gaodemap.map.companion.IconBitmapCache
42
51
  import expo.modules.gaodemap.map.utils.GeometryUtils
43
52
  import kotlin.text.StringBuilder
53
+ import kotlin.math.max
54
+ import kotlin.math.min
44
55
 
45
56
  import java.util.concurrent.CountDownLatch
46
57
  import java.util.concurrent.TimeUnit
@@ -53,6 +64,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
53
64
  // 不可交互,通过父视图定位到屏幕外
54
65
  isClickable = false
55
66
  isFocusable = false
67
+ isBaselineAligned = false
56
68
  // 设置为水平方向(默认),让子视图自然布局
57
69
  orientation = HORIZONTAL
58
70
  }
@@ -118,11 +130,98 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
118
130
  }
119
131
  }
120
132
 
121
- try {
133
+ if (childCount == 0) {
122
134
  super.onMeasure(widthMeasureSpec, heightMeasureSpec)
123
- } catch (e: Exception) {
124
- throw e
135
+ return
136
+ }
137
+
138
+ val parentWidthSize = MeasureSpec.getSize(widthMeasureSpec)
139
+ val parentHeightSize = MeasureSpec.getSize(heightMeasureSpec)
140
+ val fallbackWidthSize = resolveExplicitMeasureSize(parentWidthSize, true)
141
+ val fallbackHeightSize = resolveExplicitMeasureSize(parentHeightSize, false)
142
+
143
+ val contentWidthSpec = when {
144
+ customViewWidth > 0 -> MeasureSpec.makeMeasureSpec(customViewWidth, MeasureSpec.EXACTLY)
145
+ fallbackWidthSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackWidthSize, MeasureSpec.AT_MOST)
146
+ else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
147
+ }
148
+ val contentHeightSpec = when {
149
+ customViewHeight > 0 -> MeasureSpec.makeMeasureSpec(customViewHeight, MeasureSpec.EXACTLY)
150
+ fallbackHeightSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackHeightSize, MeasureSpec.AT_MOST)
151
+ else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
152
+ }
153
+
154
+ var measuredContentWidth = 0
155
+ var measuredContentHeight = 0
156
+
157
+ for (i in 0 until childCount) {
158
+ val child = getChildAt(i)
159
+ val lp = child.layoutParams as? LayoutParams
160
+ ?: LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
161
+
162
+ val childWidthSpec = getChildMeasureSpec(
163
+ contentWidthSpec,
164
+ paddingLeft + paddingRight,
165
+ lp.width
166
+ )
167
+ val childHeightSpec = getChildMeasureSpec(
168
+ contentHeightSpec,
169
+ paddingTop + paddingBottom,
170
+ lp.height
171
+ )
172
+
173
+ child.measure(childWidthSpec, childHeightSpec)
174
+ val childBounds = computeContentBounds(child)
175
+ measuredContentWidth = max(measuredContentWidth, childBounds?.width() ?: child.measuredWidth)
176
+ measuredContentHeight = max(measuredContentHeight, childBounds?.height() ?: child.measuredHeight)
125
177
  }
178
+
179
+ val desiredWidth = if (customViewWidth > 0) {
180
+ customViewWidth
181
+ } else {
182
+ measuredContentWidth + paddingLeft + paddingRight
183
+ }
184
+ val desiredHeight = if (customViewHeight > 0) {
185
+ customViewHeight
186
+ } else {
187
+ measuredContentHeight + paddingTop + paddingBottom
188
+ }
189
+
190
+ val finalWidth = if (parentWidthSize > 0) min(desiredWidth, parentWidthSize) else desiredWidth
191
+ val finalHeight = if (parentHeightSize > 0) min(desiredHeight, parentHeightSize) else desiredHeight
192
+
193
+ setMeasuredDimension(
194
+ max(finalWidth, suggestedMinimumWidth),
195
+ max(finalHeight, suggestedMinimumHeight)
196
+ )
197
+ }
198
+
199
+ private fun resolveExplicitMeasureSize(parentSize: Int, isWidth: Boolean): Int {
200
+ if (parentSize > 0) {
201
+ return parentSize
202
+ }
203
+
204
+ val parentView = parent as? View
205
+ val parentMeasuredSize = if (isWidth) {
206
+ parentView?.measuredWidth ?: 0
207
+ } else {
208
+ parentView?.measuredHeight ?: 0
209
+ }
210
+ if (parentMeasuredSize > 0) {
211
+ return parentMeasuredSize
212
+ }
213
+
214
+ val parentLayoutSize = if (isWidth) {
215
+ parentView?.width ?: 0
216
+ } else {
217
+ parentView?.height ?: 0
218
+ }
219
+ if (parentLayoutSize > 0) {
220
+ return parentLayoutSize
221
+ }
222
+
223
+ val displayMetrics = context.resources.displayMetrics
224
+ return if (isWidth) displayMetrics.widthPixels else displayMetrics.heightPixels
126
225
  }
127
226
 
128
227
  private val onMarkerPress by EventDispatcher()
@@ -141,6 +240,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
141
240
  private var customViewHeight: Int = 0 // 用于自定义视图(children)的高度
142
241
  private val mainHandler = Handler(Looper.getMainLooper())
143
242
  private var isRemoving = false // 标记是否正在被移除
243
+ private var pendingMarkerIconUpdate: Runnable? = null
244
+ private var lastAppliedCustomMarkerKey: String? = null
144
245
 
145
246
  // 缓存属性,在 marker 创建前保存
146
247
  private var pendingTitle: String? = null
@@ -674,7 +775,6 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
674
775
  // 只有在没有自定义内容(children)且有 title 或 snippet 时才显示信息窗口
675
776
  // 如果有自定义内容,说明用户已经自定义了显示内容,不需要默认信息窗口
676
777
  return !(view.isEmpty() && (!marker.title.isNullOrEmpty() || !marker.snippet.isNullOrEmpty()))
677
- // marker.showInfoWindow()
678
778
  }
679
779
  return false
680
780
  }
@@ -783,13 +883,20 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
783
883
  */
784
884
  private fun createBitmapFromView(): Bitmap? {
785
885
  if (isEmpty()) return null
886
+ if (hasPendingAsyncImageContent(this)) {
887
+ return null
888
+ }
786
889
 
787
- // 优先使用 JS 传入的 cacheKey,如果没有则 fallback fingerprint
788
- val keyPart = cacheKey ?: computeViewFingerprint(this)
890
+ // cacheKey 作为命名空间,真正的 children 缓存还要叠加当前内容指纹,
891
+ // 避免首次空白截图被稳定 cacheKey 永久复用。
892
+ val fingerprint = computeViewFingerprint(this)
893
+ val keyPart = cacheKey?.let { "$it|$fingerprint" } ?: fingerprint
789
894
 
790
895
  val measuredChild = if (isNotEmpty()) getChildAt(0) else null
791
- val measuredWidth = measuredChild?.measuredWidth ?: 0
792
- val measuredHeight = measuredChild?.measuredHeight ?: 0
896
+ val contentView = resolveRenderableContentView(measuredChild)
897
+ val contentBounds = computeContentBounds(measuredChild)
898
+ val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: measuredChild?.measuredWidth ?: 0
899
+ val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: measuredChild?.measuredHeight ?: 0
793
900
 
794
901
  val finalWidth = if (measuredWidth > 0) measuredWidth else (if (customViewWidth > 0) customViewWidth else 0)
795
902
  val finalHeight = if (measuredHeight > 0) measuredHeight else (if (customViewHeight > 0) customViewHeight else 0)
@@ -872,13 +979,85 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
872
979
  childView.isDrawingCacheEnabled = false
873
980
  childView.destroyDrawingCache()
874
981
 
875
- return bitmap
982
+ val shouldTrimTransparentPadding = customViewWidth <= 0 && customViewHeight <= 0
983
+ return if (shouldTrimTransparentPadding) trimTransparentPadding(bitmap) else bitmap
876
984
  } catch (_: Exception) {
877
985
  // 遇到异常时返回 null,让上层使用默认图标
878
986
  return null
879
987
  }
880
988
  }
881
989
 
990
+ private fun trimTransparentPadding(bitmap: Bitmap): Bitmap {
991
+ if (bitmap.width <= 1 || bitmap.height <= 1) {
992
+ return bitmap
993
+ }
994
+
995
+ if (hasOpaquePixelsOnAllBitmapEdges(bitmap)) {
996
+ return bitmap
997
+ }
998
+
999
+ var minX = bitmap.width
1000
+ var minY = bitmap.height
1001
+ var maxX = -1
1002
+ var maxY = -1
1003
+
1004
+ for (y in 0 until bitmap.height) {
1005
+ for (x in 0 until bitmap.width) {
1006
+ if (Color.alpha(bitmap.getPixel(x, y)) != 0) {
1007
+ if (x < minX) minX = x
1008
+ if (y < minY) minY = y
1009
+ if (x > maxX) maxX = x
1010
+ if (y > maxY) maxY = y
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ if (maxX < minX || maxY < minY) {
1016
+ return bitmap
1017
+ }
1018
+
1019
+ val trimmedWidth = maxX - minX + 1
1020
+ val trimmedHeight = maxY - minY + 1
1021
+ if (trimmedWidth == bitmap.width && trimmedHeight == bitmap.height) {
1022
+ return bitmap
1023
+ }
1024
+
1025
+ return Bitmap.createBitmap(bitmap, minX, minY, trimmedWidth, trimmedHeight)
1026
+ }
1027
+
1028
+ private fun hasOpaquePixelsOnAllBitmapEdges(bitmap: Bitmap): Boolean {
1029
+ var topEdgeHasPixel = false
1030
+ var bottomEdgeHasPixel = false
1031
+ var leftEdgeHasPixel = false
1032
+ var rightEdgeHasPixel = false
1033
+
1034
+ for (x in 0 until bitmap.width) {
1035
+ if (!topEdgeHasPixel && Color.alpha(bitmap.getPixel(x, 0)) != 0) {
1036
+ topEdgeHasPixel = true
1037
+ }
1038
+ if (!bottomEdgeHasPixel && Color.alpha(bitmap.getPixel(x, bitmap.height - 1)) != 0) {
1039
+ bottomEdgeHasPixel = true
1040
+ }
1041
+ if (topEdgeHasPixel && bottomEdgeHasPixel) {
1042
+ break
1043
+ }
1044
+ }
1045
+
1046
+ for (y in 0 until bitmap.height) {
1047
+ if (!leftEdgeHasPixel && Color.alpha(bitmap.getPixel(0, y)) != 0) {
1048
+ leftEdgeHasPixel = true
1049
+ }
1050
+ if (!rightEdgeHasPixel && Color.alpha(bitmap.getPixel(bitmap.width - 1, y)) != 0) {
1051
+ rightEdgeHasPixel = true
1052
+ }
1053
+ if (leftEdgeHasPixel && rightEdgeHasPixel) {
1054
+ break
1055
+ }
1056
+ }
1057
+
1058
+ return topEdgeHasPixel && bottomEdgeHasPixel && leftEdgeHasPixel && rightEdgeHasPixel
1059
+ }
1060
+
882
1061
  /**
883
1062
  * 更新 marker 图标
884
1063
  */
@@ -886,6 +1065,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
886
1065
  if (isEmpty()) {
887
1066
  // 如果确实为空(没有子视图),恢复默认样式
888
1067
  marker?.setIcon(BitmapDescriptorFactory.defaultMarker())
1068
+ lastAppliedCustomMarkerKey = null
889
1069
  // 恢复默认锚点(底部中心),除非用户指定了锚点
890
1070
  val anchorX = pendingAnchor?.first ?: 0.5f
891
1071
  val anchorY = pendingAnchor?.second ?: 1.0f
@@ -894,21 +1074,31 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
894
1074
  return
895
1075
  }
896
1076
 
897
- // 构建缓存 key(优先 JS 端 cacheKey)
898
- val keyPart = cacheKey ?: computeViewFingerprint(this)
1077
+ // cacheKey 作为命名空间,真正的 children 缓存还要叠加当前内容指纹。
1078
+ val fingerprint = computeViewFingerprint(this)
1079
+ val keyPart = cacheKey?.let { "$it|$fingerprint" } ?: fingerprint
899
1080
  val child = getChildAt(0)
900
- val measuredWidth = child?.measuredWidth ?: customViewWidth
901
- val measuredHeight = child?.measuredHeight ?: customViewHeight
1081
+ val contentView = resolveRenderableContentView(child)
1082
+ val contentBounds = computeContentBounds(child)
1083
+ val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: child?.measuredWidth ?: customViewWidth
1084
+ val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: child?.measuredHeight ?: customViewHeight
902
1085
  val fullCacheKey = "$keyPart|${measuredWidth}x${measuredHeight}"
903
1086
 
904
1087
  // 确定锚点:优先使用用户指定的 pendingAnchor,否则对于自定义 View 使用中心点 (0.5, 0.5)
905
1088
  val anchorX = pendingAnchor?.first ?: 0.5f
906
1089
  val anchorY = pendingAnchor?.second ?: 0.5f
907
1090
 
1091
+ if (fullCacheKey == lastAppliedCustomMarkerKey) {
1092
+ marker?.setAnchor(anchorX, anchorY)
1093
+ marker?.let { showMarker(it) }
1094
+ return
1095
+ }
1096
+
908
1097
  // 1) 尝试 BitmapDescriptor 缓存
909
1098
  BitmapDescriptorCache.get(fullCacheKey)?.let { it ->
910
1099
  marker?.setIcon(it)
911
1100
  marker?.setAnchor(anchorX, anchorY)
1101
+ lastAppliedCustomMarkerKey = fullCacheKey
912
1102
  marker?.let { showMarker(it) }
913
1103
  return
914
1104
  }
@@ -932,9 +1122,31 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
932
1122
  // 设置到 Marker
933
1123
  marker?.setIcon(descriptor)
934
1124
  marker?.setAnchor(anchorX, anchorY)
1125
+ lastAppliedCustomMarkerKey = fullCacheKey
935
1126
  marker?.let { showMarker(it) }
936
1127
  }
937
1128
 
1129
+ private fun scheduleMarkerIconUpdate(delayMs: Long = 16L) {
1130
+ if (isRemoving || marker == null || isEmpty()) {
1131
+ return
1132
+ }
1133
+
1134
+ pendingMarkerIconUpdate?.let { mainHandler.removeCallbacks(it) }
1135
+ val task = Runnable {
1136
+ pendingMarkerIconUpdate = null
1137
+ if (!isRemoving && marker != null && isNotEmpty()) {
1138
+ updateMarkerIcon()
1139
+ }
1140
+ }
1141
+ pendingMarkerIconUpdate = task
1142
+
1143
+ if (delayMs <= 0L) {
1144
+ mainHandler.post(task)
1145
+ } else {
1146
+ mainHandler.postDelayed(task, delayMs)
1147
+ }
1148
+ }
1149
+
938
1150
 
939
1151
 
940
1152
  override fun removeView(child: View?) {
@@ -956,11 +1168,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
956
1168
  super.removeViewAt(index)
957
1169
  // 只在还有子视图时更新图标
958
1170
  if (!isRemoving && childCount > 1 && marker != null) {
959
- mainHandler.postDelayed({
960
- if (!isRemoving && marker != null && isNotEmpty()) {
961
- updateMarkerIcon()
962
- }
963
- }, 50)
1171
+ scheduleMarkerIconUpdate(50)
964
1172
  }
965
1173
  // 如果最后一个子视图被移除,什么都不做
966
1174
  // 让 onDetachedFromWindow 处理完整的清理
@@ -969,35 +1177,82 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
969
1177
  // 忽略异常
970
1178
  }
971
1179
  }
972
- /**
973
- * 递归修复子视图的 LayoutParams,确保所有子视图都使用正确的 LayoutParams 类型
974
- */
975
- private fun fixChildLayoutParams(view: View) {
976
- if (view is ViewGroup) {
977
- for (i in 0 until view.childCount) {
978
- val child = view.getChildAt(i)
979
- val currentParams = child.layoutParams
980
- if (currentParams != null && currentParams !is LayoutParams) {
981
- child.layoutParams = LayoutParams(
982
- currentParams.width,
983
- currentParams.height
984
- )
985
- }
986
- fixChildLayoutParams(child)
1180
+ private fun resolveRenderableContentView(view: View?): View? {
1181
+ var current = view ?: return null
1182
+
1183
+ while (current is ViewGroup && current.childCount == 1) {
1184
+ val next = current.getChildAt(0) ?: break
1185
+ current = next
1186
+ }
1187
+
1188
+ return current
1189
+ }
1190
+
1191
+ private fun computeContentBounds(view: View?): Rect? {
1192
+ view ?: return null
1193
+ if (view.visibility != View.VISIBLE) return null
1194
+
1195
+ var resolvedBounds: Rect? = null
1196
+
1197
+ if (view is ViewGroup && view.childCount > 0) {
1198
+ for (i in 0 until view.childCount) {
1199
+ val child = view.getChildAt(i) ?: continue
1200
+ val childBounds = computeContentBounds(child) ?: continue
1201
+ val shiftedBounds = Rect(childBounds)
1202
+ shiftedBounds.offset(child.left, child.top)
1203
+ resolvedBounds = if (resolvedBounds == null) {
1204
+ shiftedBounds
1205
+ } else {
1206
+ Rect(resolvedBounds).apply { union(shiftedBounds) }
987
1207
  }
1208
+ }
988
1209
  }
1210
+
1211
+ val hasOwnVisualBounds =
1212
+ view.background != null ||
1213
+ view.paddingLeft != 0 ||
1214
+ view.paddingTop != 0 ||
1215
+ view.paddingRight != 0 ||
1216
+ view.paddingBottom != 0 ||
1217
+ view !is ViewGroup
1218
+
1219
+ val ownWidth = view.measuredWidth.takeIf { it > 0 } ?: view.width
1220
+ val ownHeight = view.measuredHeight.takeIf { it > 0 } ?: view.height
1221
+
1222
+ if (hasOwnVisualBounds && ownWidth > 0 && ownHeight > 0) {
1223
+ val ownBounds = Rect(0, 0, ownWidth, ownHeight)
1224
+ resolvedBounds = if (resolvedBounds == null) {
1225
+ ownBounds
1226
+ } else {
1227
+ Rect(resolvedBounds).apply { union(ownBounds) }
1228
+ }
1229
+ }
1230
+
1231
+ return resolvedBounds
989
1232
  }
990
1233
 
991
1234
 
992
1235
  override fun addView(child: View?, index: Int, params: android.view.ViewGroup.LayoutParams?) {
993
1236
  // 🔑 关键修复:记录添加前的子视图数量
994
1237
  val childCountBefore = childCount
1238
+
1239
+ val sourceWidth = when {
1240
+ customViewWidth > 0 -> customViewWidth
1241
+ params?.width != null && params.width > 0 -> params.width
1242
+ else -> LayoutParams.WRAP_CONTENT
1243
+ }
1244
+
1245
+ val sourceHeight = when {
1246
+ customViewHeight > 0 -> customViewHeight
1247
+ params?.height != null && params.height > 0 -> params.height
1248
+ else -> LayoutParams.WRAP_CONTENT
1249
+ }
995
1250
 
996
1251
  val finalParams = LayoutParams(
997
- if (customViewWidth > 0) customViewWidth else LayoutParams.WRAP_CONTENT,
998
- if (customViewHeight > 0) customViewHeight else LayoutParams.WRAP_CONTENT
1252
+ sourceWidth,
1253
+ sourceHeight
999
1254
  )
1000
-
1255
+
1001
1256
  super.addView(child, index, finalParams)
1002
1257
 
1003
1258
  child?.let {
@@ -1008,17 +1263,12 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1008
1263
  childParams?.height ?: LayoutParams.WRAP_CONTENT
1009
1264
  )
1010
1265
  }
1011
- fixChildLayoutParams(it)
1012
1266
  }
1013
1267
 
1014
1268
  // 🔑 修复:需要延迟更新图标,等待 children 完成布局
1015
1269
  // 原因:立即更新会在 children 还未完成测量/布局时就渲染,导致内容为空
1016
1270
  if (!isRemoving && marker != null && childCount > childCountBefore) {
1017
- mainHandler.post {
1018
- if (!isRemoving && marker != null && isNotEmpty()) {
1019
- updateMarkerIcon()
1020
- }
1021
- }
1271
+ scheduleMarkerIconUpdate()
1022
1272
  }
1023
1273
  }
1024
1274
 
@@ -1027,11 +1277,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1027
1277
  // 🔑 修复:布局完成后延迟更新图标
1028
1278
  // 即使 changed 为 false,只要有内容,也应该检查是否需要更新(例如子 View 尺寸变化但 MarkerView 没变)
1029
1279
  if (!isRemoving && isNotEmpty() && marker != null) {
1030
- mainHandler.post {
1031
- if (!isRemoving && marker != null && isNotEmpty()) {
1032
- updateMarkerIcon()
1033
- }
1034
- }
1280
+ scheduleMarkerIconUpdate()
1035
1281
  }
1036
1282
  }
1037
1283
 
@@ -1238,6 +1484,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1238
1484
  private fun stopSmoothMove() {
1239
1485
  smoothMoveMarker?.stopMove()
1240
1486
  smoothMoveMarker?.setVisible(false)
1487
+ lastAppliedCustomMarkerKey = null
1241
1488
  marker?.let { showMarker(it) }
1242
1489
  }
1243
1490
 
@@ -1263,6 +1510,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1263
1510
  // 🔑 关键修复:使用 post 延迟检查
1264
1511
  // 清理所有延迟任务
1265
1512
  mainHandler.removeCallbacksAndMessages(null)
1513
+ pendingMarkerIconUpdate = null
1266
1514
 
1267
1515
  // 延迟检查 parent 状态
1268
1516
  mainHandler.post {
@@ -1338,4 +1586,27 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1338
1586
  // 最终返回一个截断的 sha-like 形式(避免 key 过长)
1339
1587
  return sb.toString().take(1024)
1340
1588
  }
1589
+
1590
+ private fun hasPendingAsyncImageContent(view: View?): Boolean {
1591
+ view ?: return false
1592
+ if (view.visibility != View.VISIBLE) return false
1593
+
1594
+ if (view is ImageView) {
1595
+ val resolvedWidth = view.measuredWidth.takeIf { it > 0 } ?: view.width
1596
+ val resolvedHeight = view.measuredHeight.takeIf { it > 0 } ?: view.height
1597
+ if (resolvedWidth > 0 && resolvedHeight > 0 && view.drawable == null) {
1598
+ return true
1599
+ }
1600
+ }
1601
+
1602
+ if (view is ViewGroup) {
1603
+ for (i in 0 until view.childCount) {
1604
+ if (hasPendingAsyncImageContent(view.getChildAt(i))) {
1605
+ return true
1606
+ }
1607
+ }
1608
+ }
1609
+
1610
+ return false
1611
+ }
1341
1612
  }
@@ -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
  // 平滑移动路径
package/build/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import ExpoGaodeMapNavigationModule from './ExpoGaodeMapNavigationModule';
2
2
  export * from './map';
3
- import { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy } from './types';
4
- import type { NaviPoint, RouteOptions, DriveRouteOptions, WalkRouteOptions, RideRouteOptions, EBikeRouteOptions, TruckRouteOptions, RouteResult, DriveRouteResult, IndependentRouteResult, IndependentDriveRouteOptions, IndependentTruckRouteOptions, IndependentWalkRouteOptions, IndependentRideRouteOptions, SelectIndependentRouteOptions, StartNaviWithIndependentPathOptions, ClearIndependentRouteOptions, MotorcycleRouteOptions, IndependentMotorcycleRouteOptions } from './types';
3
+ import { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, type TransitRouteOptions } from './types';
4
+ import type { NaviPoint, RouteOptions, DriveRouteOptions, WalkRouteOptions, RideRouteOptions, EBikeRouteOptions, TransitRouteOptions as TransitRouteOptionsType, TruckRouteOptions, RouteResult, DriveRouteResult, IndependentRouteResult, IndependentDriveRouteOptions, IndependentTruckRouteOptions, IndependentWalkRouteOptions, IndependentRideRouteOptions, SelectIndependentRouteOptions, StartNaviWithIndependentPathOptions, ClearIndependentRouteOptions, MotorcycleRouteOptions, IndependentMotorcycleRouteOptions } from './types';
5
5
  export { ExpoGaodeMapNaviView, type ExpoGaodeMapNaviViewRef, ExpoGaodeMapNaviView as NaviView, type ExpoGaodeMapNaviViewRef as NaviViewRef } from './ExpoGaodeMapNaviView';
6
6
  /**
7
7
  * 初始化导航模块(可选)
@@ -14,7 +14,6 @@ export declare const initNavigation: () => void;
14
14
  export declare const destroyAllCalculators: () => void;
15
15
  /**
16
16
  * 路径规划(通用方法)
17
- * 注意:公交路径规划暂未实现
18
17
  */
19
18
  export declare function calculateRoute(options: RouteOptions): Promise<RouteResult | DriveRouteResult>;
20
19
  /**
@@ -41,6 +40,10 @@ export declare const calculateTruckRoute: (options: TruckRouteOptions) => Promis
41
40
  * 摩托车路径规划(车类型为 11,支持传入排量)
42
41
  */
43
42
  export declare const calculateMotorcycleRoute: (options: MotorcycleRouteOptions) => Promise<DriveRouteResult>;
43
+ /**
44
+ * 公交换乘路径规划(运行时 fallback 到 Web API)
45
+ */
46
+ export declare function calculateTransitRoute(options: TransitRouteOptions): Promise<DriveRouteResult>;
44
47
  /**
45
48
  * 独立路径规划(不会影响当前导航;适合路线预览/行前选路)
46
49
  */
@@ -64,7 +67,7 @@ export declare const startNaviWithIndependentPath: (options: StartNaviWithIndepe
64
67
  * 独立路径组:清理
65
68
  */
66
69
  export declare const clearIndependentRoute: (options: ClearIndependentRouteOptions) => Promise<boolean>;
67
- export type { NaviPoint, RouteOptions, DriveRouteOptions, WalkRouteOptions, RideRouteOptions, EBikeRouteOptions, TruckRouteOptions, RouteResult, DriveRouteResult, IndependentRouteResult, IndependentDriveRouteOptions, IndependentTruckRouteOptions, IndependentWalkRouteOptions, IndependentRideRouteOptions, SelectIndependentRouteOptions, StartNaviWithIndependentPathOptions, ClearIndependentRouteOptions, MotorcycleRouteOptions, IndependentMotorcycleRouteOptions, };
70
+ export type { NaviPoint, RouteOptions, DriveRouteOptions, WalkRouteOptions, RideRouteOptions, EBikeRouteOptions, TransitRouteOptionsType as TransitRouteOptions, TruckRouteOptions, RouteResult, DriveRouteResult, IndependentRouteResult, IndependentDriveRouteOptions, IndependentTruckRouteOptions, IndependentWalkRouteOptions, IndependentRideRouteOptions, SelectIndependentRouteOptions, StartNaviWithIndependentPathOptions, ClearIndependentRouteOptions, MotorcycleRouteOptions, IndependentMotorcycleRouteOptions, };
68
71
  export { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, };
69
72
  declare const _default: {
70
73
  initNavigation: () => void;
@@ -74,6 +77,7 @@ declare const _default: {
74
77
  calculateWalkRoute: (options: WalkRouteOptions) => Promise<RouteResult>;
75
78
  calculateRideRoute: (options: RideRouteOptions) => Promise<RouteResult>;
76
79
  calculateEBikeRoute: (options: EBikeRouteOptions) => Promise<RouteResult>;
80
+ calculateTransitRoute: typeof calculateTransitRoute;
77
81
  calculateTruckRoute: (options: TruckRouteOptions) => Promise<DriveRouteResult>;
78
82
  calculateMotorcycleRoute: (options: MotorcycleRouteOptions) => Promise<DriveRouteResult>;
79
83
  independentDriveRoute: (options: IndependentDriveRouteOptions) => Promise<IndependentRouteResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}