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.
- package/README.md +52 -1
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +182 -86
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +5 -2
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +19 -5
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +319 -48
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +3 -3
- package/build/index.d.ts +8 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +79 -1
- package/build/index.js.map +1 -1
- package/build/map/ExpoGaodeMapModule.d.ts +4 -4
- package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapModule.js +10 -8
- package/build/map/ExpoGaodeMapModule.js.map +1 -1
- package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapView.js +79 -17
- package/build/map/ExpoGaodeMapView.js.map +1 -1
- package/build/map/components/overlays/Cluster.d.ts.map +1 -1
- package/build/map/components/overlays/Cluster.js +12 -0
- package/build/map/components/overlays/Cluster.js.map +1 -1
- package/build/map/components/overlays/Marker.d.ts.map +1 -1
- package/build/map/components/overlays/Marker.js +70 -6
- package/build/map/components/overlays/Marker.js.map +1 -1
- package/build/map/types/common.types.d.ts +29 -5
- package/build/map/types/common.types.d.ts.map +1 -1
- package/build/map/types/common.types.js +5 -5
- package/build/map/types/common.types.js.map +1 -1
- package/build/map/types/index.d.ts +2 -1
- package/build/map/types/index.d.ts.map +1 -1
- package/build/map/types/index.js.map +1 -1
- package/build/map/types/location.types.d.ts +23 -0
- package/build/map/types/location.types.d.ts.map +1 -1
- package/build/map/types/location.types.js.map +1 -1
- package/build/map/types/map-view.types.d.ts +20 -22
- package/build/map/types/map-view.types.d.ts.map +1 -1
- package/build/map/types/map-view.types.js.map +1 -1
- package/build/map/types/overlays.types.d.ts +9 -2
- package/build/map/types/overlays.types.d.ts.map +1 -1
- package/build/map/types/overlays.types.js.map +1 -1
- package/build/map/types/route-playback.types.d.ts +12 -0
- package/build/map/types/route-playback.types.d.ts.map +1 -0
- package/build/map/types/route-playback.types.js +2 -0
- package/build/map/types/route-playback.types.js.map +1 -0
- package/build/types/route.types.d.ts +10 -1
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js +2 -0
- package/build/types/route.types.js.map +1 -1
- package/ios/map/ExpoGaodeMapView.swift +151 -76
- package/ios/map/ExpoGaodeMapViewModule.swift +14 -1
- package/ios/map/managers/UIManager.swift +5 -4
- package/ios/map/overlays/ClusterView.swift +207 -147
- package/ios/map/overlays/ClusterViewModule.swift +5 -1
- package/ios/map/overlays/MarkerView.swift +214 -60
- package/ios/map/overlays/MarkerViewModule.swift +1 -1
- 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
|
-
|
|
133
|
+
if (childCount == 0) {
|
|
122
134
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
//
|
|
788
|
-
|
|
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
|
|
792
|
-
val
|
|
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
|
-
|
|
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
|
-
//
|
|
898
|
-
val
|
|
1077
|
+
// cacheKey 作为命名空间,真正的 children 缓存还要叠加当前内容指纹。
|
|
1078
|
+
val fingerprint = computeViewFingerprint(this)
|
|
1079
|
+
val keyPart = cacheKey?.let { "$it|$fingerprint" } ?: fingerprint
|
|
899
1080
|
val child = getChildAt(0)
|
|
900
|
-
val
|
|
901
|
-
val
|
|
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
|
-
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
-
|
|
998
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
Prop<String
|
|
82
|
-
|
|
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>;
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,4BAA4B,MAAM,gCAAgC,CAAC;AAG1E,cAAc,OAAO,CAAC;AACtB,OAAO,EACL,SAAS,EACT,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|