expo-gaode-map 2.2.29 → 2.2.30-next.1
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/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +4 -2
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +180 -61
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +7 -14
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +15 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +24 -13
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +351 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +236 -216
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
- package/build/ExpoGaodeMapModule.d.ts +13 -5
- package/build/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapModule.js +166 -15
- package/build/ExpoGaodeMapModule.js.map +1 -1
- package/build/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/ExpoGaodeMapView.js +48 -18
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/AreaMaskOverlay.d.ts +5 -0
- package/build/components/AreaMaskOverlay.d.ts.map +1 -0
- package/build/components/AreaMaskOverlay.js +20 -0
- package/build/components/AreaMaskOverlay.js.map +1 -0
- package/build/components/FoldableMapView.d.ts.map +1 -1
- package/build/components/FoldableMapView.js +115 -104
- package/build/components/FoldableMapView.js.map +1 -1
- package/build/components/RouteOverlay.d.ts +5 -0
- package/build/components/RouteOverlay.d.ts.map +1 -0
- package/build/components/RouteOverlay.js +20 -0
- package/build/components/RouteOverlay.js.map +1 -0
- package/build/components/overlays/Cluster.d.ts.map +1 -1
- package/build/components/overlays/Cluster.js +12 -0
- package/build/components/overlays/Cluster.js.map +1 -1
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +132 -6
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/hooks/useRoutePlayback.d.ts +4 -0
- package/build/hooks/useRoutePlayback.d.ts.map +1 -0
- package/build/hooks/useRoutePlayback.js +310 -0
- package/build/hooks/useRoutePlayback.js.map +1 -0
- package/build/index.d.ts +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/types/common.types.d.ts +24 -0
- package/build/types/common.types.d.ts.map +1 -1
- package/build/types/common.types.js.map +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js.map +1 -1
- package/build/types/location.types.d.ts +28 -0
- package/build/types/location.types.d.ts.map +1 -1
- package/build/types/location.types.js +5 -0
- package/build/types/location.types.js.map +1 -1
- package/build/types/map-view.types.d.ts +22 -22
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +2 -2
- package/build/types/native-module.types.d.ts.map +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/types/overlays.types.d.ts +20 -2
- package/build/types/overlays.types.d.ts.map +1 -1
- package/build/types/overlays.types.js.map +1 -1
- package/build/types/route-playback.types.d.ts +118 -0
- package/build/types/route-playback.types.d.ts.map +1 -0
- package/build/types/route-playback.types.js +2 -0
- package/build/types/route-playback.types.js.map +1 -0
- package/build/utils/RouteUtils.d.ts +8 -0
- package/build/utils/RouteUtils.d.ts.map +1 -0
- package/build/utils/RouteUtils.js +140 -0
- package/build/utils/RouteUtils.js.map +1 -0
- package/ios/ExpoGaodeMapModule.swift +41 -22
- package/ios/ExpoGaodeMapView.swift +226 -234
- package/ios/ExpoGaodeMapViewModule.swift +14 -1
- package/ios/modules/LocationManager.swift +32 -9
- package/ios/overlays/ClusterView.swift +114 -12
- package/ios/overlays/ClusterViewModule.swift +5 -1
- package/ios/overlays/MarkerView.swift +360 -69
- package/ios/overlays/MarkerViewModule.swift +7 -7
- package/package.json +6 -6
- package/build/utils/throttle.d.ts +0 -10
- package/build/utils/throttle.d.ts.map +0 -1
- package/build/utils/throttle.js +0 -19
- package/build/utils/throttle.js.map +0 -1
|
@@ -772,7 +772,7 @@ class ExpoGaodeMapModule : Module() {
|
|
|
772
772
|
// 使用 WeakReference 避免内存泄露
|
|
773
773
|
val contextRef = java.lang.ref.WeakReference(appContext.reactContext)
|
|
774
774
|
val handler = android.os.Handler(android.os.Looper.getMainLooper())
|
|
775
|
-
|
|
775
|
+
var attempts = 0
|
|
776
776
|
val maxAttempts = 50 // 增加到 5 秒 / 100ms,给用户足够时间操作
|
|
777
777
|
|
|
778
778
|
val checkPermission = object : Runnable {
|
|
@@ -784,6 +784,7 @@ class ExpoGaodeMapModule : Module() {
|
|
|
784
784
|
}
|
|
785
785
|
|
|
786
786
|
val status = PermissionHelper.checkForegroundLocationPermission(context)
|
|
787
|
+
attempts += 1
|
|
787
788
|
|
|
788
789
|
// 如果权限已授予或达到最大尝试次数,返回结果并清理 Handler
|
|
789
790
|
if (status.granted || attempts >= maxAttempts) {
|
|
@@ -843,7 +844,7 @@ class ExpoGaodeMapModule : Module() {
|
|
|
843
844
|
// 轮询检查权限状态
|
|
844
845
|
val contextRef = java.lang.ref.WeakReference(appContext.reactContext)
|
|
845
846
|
val handler = android.os.Handler(android.os.Looper.getMainLooper())
|
|
846
|
-
|
|
847
|
+
var attempts = 0
|
|
847
848
|
val maxAttempts = 30
|
|
848
849
|
|
|
849
850
|
val checkPermission = object : Runnable {
|
|
@@ -855,6 +856,7 @@ class ExpoGaodeMapModule : Module() {
|
|
|
855
856
|
}
|
|
856
857
|
|
|
857
858
|
val status = PermissionHelper.checkBackgroundLocationPermission(context)
|
|
859
|
+
attempts += 1
|
|
858
860
|
|
|
859
861
|
if (status.granted || attempts >= maxAttempts) {
|
|
860
862
|
handler.removeCallbacks(this)
|
|
@@ -2,6 +2,7 @@ package expo.modules.gaodemap
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.os.SystemClock
|
|
5
6
|
import android.view.View
|
|
6
7
|
import android.view.ViewGroup
|
|
7
8
|
import com.amap.api.maps.AMap
|
|
@@ -32,6 +33,10 @@ import androidx.core.graphics.withTranslation
|
|
|
32
33
|
@SuppressLint("ViewConstructor")
|
|
33
34
|
class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
34
35
|
|
|
36
|
+
init {
|
|
37
|
+
orientation = VERTICAL
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
/**
|
|
36
41
|
* 拦截 React Native 的 ViewManager 操作
|
|
37
42
|
* 重写 requestLayout 防止在移除视图时触发布局异常
|
|
@@ -44,6 +49,63 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
53
|
+
val measuredWidth = View.MeasureSpec.getSize(widthMeasureSpec)
|
|
54
|
+
val measuredHeight = View.MeasureSpec.getSize(heightMeasureSpec)
|
|
55
|
+
|
|
56
|
+
setMeasuredDimension(measuredWidth, measuredHeight)
|
|
57
|
+
|
|
58
|
+
for (i in 0 until childCount) {
|
|
59
|
+
val child = getChildAt(i) ?: continue
|
|
60
|
+
if (child.visibility == View.GONE) {
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (child === mapView) {
|
|
65
|
+
val childWidthSpec = View.MeasureSpec.makeMeasureSpec(measuredWidth, View.MeasureSpec.EXACTLY)
|
|
66
|
+
val childHeightSpec = View.MeasureSpec.makeMeasureSpec(measuredHeight, View.MeasureSpec.EXACTLY)
|
|
67
|
+
child.measure(childWidthSpec, childHeightSpec)
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
val lp = child.layoutParams
|
|
72
|
+
val childWidthSpec = when {
|
|
73
|
+
lp?.width != null && lp.width > 0 ->
|
|
74
|
+
View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY)
|
|
75
|
+
else ->
|
|
76
|
+
View.MeasureSpec.makeMeasureSpec(measuredWidth, View.MeasureSpec.AT_MOST)
|
|
77
|
+
}
|
|
78
|
+
val childHeightSpec = when {
|
|
79
|
+
lp?.height != null && lp.height > 0 ->
|
|
80
|
+
View.MeasureSpec.makeMeasureSpec(lp.height, View.MeasureSpec.EXACTLY)
|
|
81
|
+
else ->
|
|
82
|
+
View.MeasureSpec.makeMeasureSpec(measuredHeight, View.MeasureSpec.AT_MOST)
|
|
83
|
+
}
|
|
84
|
+
child.measure(childWidthSpec, childHeightSpec)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
89
|
+
val width = right - left
|
|
90
|
+
val height = bottom - top
|
|
91
|
+
|
|
92
|
+
for (i in 0 until childCount) {
|
|
93
|
+
val child = getChildAt(i) ?: continue
|
|
94
|
+
if (child.visibility == View.GONE) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (child === mapView) {
|
|
99
|
+
child.layout(0, 0, width, height)
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
val childWidth = child.measuredWidth
|
|
104
|
+
val childHeight = child.measuredHeight
|
|
105
|
+
child.layout(0, 0, childWidth, childHeight)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
47
109
|
// Props 存储
|
|
48
110
|
/** 地图类型 */
|
|
49
111
|
internal var mapType: Int = 0
|
|
@@ -51,6 +113,12 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
51
113
|
internal var initialCameraPosition: Map<String, Any?>? = null
|
|
52
114
|
/** 是否跟随用户位置 */
|
|
53
115
|
internal var followUserLocation: Boolean = false
|
|
116
|
+
/** 是否显示底图文字标注 */
|
|
117
|
+
private var labelsEnabled: Boolean = true
|
|
118
|
+
/** 是否显示定位按钮 */
|
|
119
|
+
private var myLocationButtonEnabled: Boolean = false
|
|
120
|
+
/** 相机移动事件节流间隔 */
|
|
121
|
+
private var cameraEventThrottleMs: Long = 32L
|
|
54
122
|
/** 自定义地图样式配置(缓存) */
|
|
55
123
|
private var customMapStyleData: Map<String, Any>? = null
|
|
56
124
|
|
|
@@ -59,6 +127,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
59
127
|
|
|
60
128
|
// 事件派发器
|
|
61
129
|
private val onMapPress by EventDispatcher()
|
|
130
|
+
private val onPressPoi by EventDispatcher()
|
|
62
131
|
private val onMapLongPress by EventDispatcher()
|
|
63
132
|
private val onLoad by EventDispatcher()
|
|
64
133
|
private val onLocation by EventDispatcher()
|
|
@@ -67,6 +136,8 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
67
136
|
|
|
68
137
|
// 缓存的相机移动事件数据
|
|
69
138
|
private var pendingCameraMoveData: Map<String, Any>? = null
|
|
139
|
+
private var pendingCameraMoveDispatch: Runnable? = null
|
|
140
|
+
private var lastCameraMoveDispatchAt: Long = 0L
|
|
70
141
|
|
|
71
142
|
// 高德地图视图
|
|
72
143
|
private lateinit var mapView: TextureMapView
|
|
@@ -130,6 +201,9 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
130
201
|
uiManager.setCustomMapStyle(styleData)
|
|
131
202
|
}
|
|
132
203
|
|
|
204
|
+
uiManager.setLabelsEnabled(labelsEnabled)
|
|
205
|
+
uiManager.setMyLocationButtonEnabled(myLocationButtonEnabled)
|
|
206
|
+
|
|
133
207
|
onLoad(mapOf("loaded" to true))
|
|
134
208
|
}
|
|
135
209
|
} catch (_: Exception) {
|
|
@@ -162,39 +236,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
162
236
|
|
|
163
237
|
// 相机移动中 - 应用节流优化
|
|
164
238
|
cameraPosition?.let {
|
|
165
|
-
|
|
166
|
-
val eventData = mapOf(
|
|
167
|
-
"cameraPosition" to mapOf(
|
|
168
|
-
"target" to mapOf(
|
|
169
|
-
"latitude" to it.target.latitude,
|
|
170
|
-
"longitude" to it.target.longitude
|
|
171
|
-
),
|
|
172
|
-
"zoom" to it.zoom,
|
|
173
|
-
"tilt" to it.tilt,
|
|
174
|
-
"bearing" to it.bearing
|
|
175
|
-
),
|
|
176
|
-
"latLngBounds" to mapOf(
|
|
177
|
-
"northeast" to mapOf(
|
|
178
|
-
"latitude" to visibleRegion.farRight.latitude,
|
|
179
|
-
"longitude" to visibleRegion.farRight.longitude
|
|
180
|
-
),
|
|
181
|
-
"southwest" to mapOf(
|
|
182
|
-
"latitude" to visibleRegion.nearLeft.latitude,
|
|
183
|
-
"longitude" to visibleRegion.nearLeft.longitude
|
|
184
|
-
)
|
|
185
|
-
)
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
// 使用 onCameraMove 自身的节流机制(如果在 Module 定义中配置了 Coalescing)
|
|
189
|
-
// 或者在这里简单发送,让 JS 端处理节流,或者依赖 Expo 的事件批处理
|
|
190
|
-
// 这里我们移除自定义的 Handler 实现,直接发送事件,简化代码逻辑
|
|
191
|
-
// 注意:高德地图的 onCameraChange 调用频率非常高,
|
|
192
|
-
// 建议在 Module 定义中使用 Events("onCameraMove") 时考虑是否需要原生侧节流
|
|
193
|
-
// 目前 Expo Modules 默认没有自动节流,但为了代码简洁和避免 Handler 泄漏风险,
|
|
194
|
-
// 我们可以依赖 JS 端的 debounce/throttle,或者如果性能是瓶颈,再加回轻量级的节流。
|
|
195
|
-
// 鉴于之前的 Handler 实现比较复杂且容易出错,我们先简化。
|
|
196
|
-
|
|
197
|
-
onCameraMove(eventData)
|
|
239
|
+
dispatchCameraMoveEvent(buildCameraEventData(it))
|
|
198
240
|
}
|
|
199
241
|
}
|
|
200
242
|
|
|
@@ -204,28 +246,8 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
204
246
|
|
|
205
247
|
// 相机移动完成
|
|
206
248
|
cameraPosition?.let {
|
|
207
|
-
|
|
208
|
-
onCameraIdle(
|
|
209
|
-
"cameraPosition" to mapOf(
|
|
210
|
-
"target" to mapOf(
|
|
211
|
-
"latitude" to it.target.latitude,
|
|
212
|
-
"longitude" to it.target.longitude
|
|
213
|
-
),
|
|
214
|
-
"zoom" to it.zoom,
|
|
215
|
-
"tilt" to it.tilt,
|
|
216
|
-
"bearing" to it.bearing
|
|
217
|
-
),
|
|
218
|
-
"latLngBounds" to mapOf(
|
|
219
|
-
"northeast" to mapOf(
|
|
220
|
-
"latitude" to visibleRegion.farRight.latitude,
|
|
221
|
-
"longitude" to visibleRegion.farRight.longitude
|
|
222
|
-
),
|
|
223
|
-
"southwest" to mapOf(
|
|
224
|
-
"latitude" to visibleRegion.nearLeft.latitude,
|
|
225
|
-
"longitude" to visibleRegion.nearLeft.longitude
|
|
226
|
-
)
|
|
227
|
-
)
|
|
228
|
-
))
|
|
249
|
+
flushPendingCameraMoveEvent()
|
|
250
|
+
onCameraIdle(buildCameraEventData(it))
|
|
229
251
|
}
|
|
230
252
|
}
|
|
231
253
|
})
|
|
@@ -292,6 +314,17 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
292
314
|
))
|
|
293
315
|
}
|
|
294
316
|
|
|
317
|
+
aMap.setOnPOIClickListener { poi ->
|
|
318
|
+
onPressPoi(mapOf(
|
|
319
|
+
"id" to poi.poiId,
|
|
320
|
+
"name" to poi.name,
|
|
321
|
+
"position" to mapOf(
|
|
322
|
+
"latitude" to poi.coordinate.latitude,
|
|
323
|
+
"longitude" to poi.coordinate.longitude
|
|
324
|
+
)
|
|
325
|
+
))
|
|
326
|
+
}
|
|
327
|
+
|
|
295
328
|
aMap.setOnMapLongClickListener { latLng ->
|
|
296
329
|
onMapLongPress(mapOf(
|
|
297
330
|
"latitude" to latLng.latitude,
|
|
@@ -395,6 +428,24 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
395
428
|
fun setShowsBuildings(show: Boolean) = uiManager.setShowsBuildings(show)
|
|
396
429
|
/** 设置是否显示室内地图 */
|
|
397
430
|
fun setShowsIndoorMap(show: Boolean) = uiManager.setShowsIndoorMap(show)
|
|
431
|
+
/** 设置是否显示底图文字标注 */
|
|
432
|
+
fun setLabelsEnabled(enabled: Boolean) {
|
|
433
|
+
labelsEnabled = enabled
|
|
434
|
+
uiManager.setLabelsEnabled(enabled)
|
|
435
|
+
}
|
|
436
|
+
/** 设置是否显示定位按钮 */
|
|
437
|
+
fun setMyLocationButtonEnabled(enabled: Boolean) {
|
|
438
|
+
myLocationButtonEnabled = enabled
|
|
439
|
+
uiManager.setMyLocationButtonEnabled(enabled)
|
|
440
|
+
}
|
|
441
|
+
/** 设置相机移动事件节流间隔 */
|
|
442
|
+
fun setCameraEventThrottleMs(throttleMs: Int) {
|
|
443
|
+
cameraEventThrottleMs = throttleMs.toLong().coerceAtLeast(0L)
|
|
444
|
+
if (cameraEventThrottleMs == 0L) {
|
|
445
|
+
pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
|
|
446
|
+
pendingCameraMoveDispatch = null
|
|
447
|
+
}
|
|
448
|
+
}
|
|
398
449
|
|
|
399
450
|
/**
|
|
400
451
|
* 设置自定义地图样式
|
|
@@ -580,9 +631,12 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
580
631
|
try {
|
|
581
632
|
// 清理 Handler 回调,防止内存泄露
|
|
582
633
|
mainHandler.removeCallbacksAndMessages(null)
|
|
634
|
+
pendingCameraMoveData = null
|
|
635
|
+
pendingCameraMoveDispatch = null
|
|
583
636
|
|
|
584
637
|
// 清理所有地图监听器
|
|
585
638
|
aMap.setOnMapClickListener(null)
|
|
639
|
+
aMap.setOnPOIClickListener(null)
|
|
586
640
|
aMap.setOnMapLongClickListener(null)
|
|
587
641
|
aMap.setOnMapLoadedListener(null)
|
|
588
642
|
aMap.setOnCameraChangeListener(null)
|
|
@@ -602,6 +656,73 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
602
656
|
|
|
603
657
|
}
|
|
604
658
|
|
|
659
|
+
private fun buildCameraEventData(cameraPosition: com.amap.api.maps.model.CameraPosition): Map<String, Any> {
|
|
660
|
+
val visibleRegion = aMap.projection.visibleRegion
|
|
661
|
+
return mapOf(
|
|
662
|
+
"cameraPosition" to mapOf(
|
|
663
|
+
"target" to mapOf(
|
|
664
|
+
"latitude" to cameraPosition.target.latitude,
|
|
665
|
+
"longitude" to cameraPosition.target.longitude
|
|
666
|
+
),
|
|
667
|
+
"zoom" to cameraPosition.zoom,
|
|
668
|
+
"tilt" to cameraPosition.tilt,
|
|
669
|
+
"bearing" to cameraPosition.bearing
|
|
670
|
+
),
|
|
671
|
+
"latLngBounds" to mapOf(
|
|
672
|
+
"northeast" to mapOf(
|
|
673
|
+
"latitude" to visibleRegion.farRight.latitude,
|
|
674
|
+
"longitude" to visibleRegion.farRight.longitude
|
|
675
|
+
),
|
|
676
|
+
"southwest" to mapOf(
|
|
677
|
+
"latitude" to visibleRegion.nearLeft.latitude,
|
|
678
|
+
"longitude" to visibleRegion.nearLeft.longitude
|
|
679
|
+
)
|
|
680
|
+
)
|
|
681
|
+
)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
private fun dispatchCameraMoveEvent(eventData: Map<String, Any>) {
|
|
685
|
+
val throttleMs = cameraEventThrottleMs.coerceAtLeast(0L)
|
|
686
|
+
if (throttleMs == 0L) {
|
|
687
|
+
pendingCameraMoveData = null
|
|
688
|
+
pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
|
|
689
|
+
pendingCameraMoveDispatch = null
|
|
690
|
+
lastCameraMoveDispatchAt = SystemClock.uptimeMillis()
|
|
691
|
+
onCameraMove(eventData)
|
|
692
|
+
return
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
pendingCameraMoveData = eventData
|
|
696
|
+
|
|
697
|
+
val now = SystemClock.uptimeMillis()
|
|
698
|
+
val elapsed = now - lastCameraMoveDispatchAt
|
|
699
|
+
if (elapsed >= throttleMs) {
|
|
700
|
+
pendingCameraMoveDispatch?.let(mainHandler::removeCallbacks)
|
|
701
|
+
pendingCameraMoveDispatch = null
|
|
702
|
+
flushPendingCameraMoveEvent(now)
|
|
703
|
+
return
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (pendingCameraMoveDispatch != null) {
|
|
707
|
+
return
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
val delay = (throttleMs - elapsed).coerceAtLeast(0L)
|
|
711
|
+
val runnable = Runnable {
|
|
712
|
+
pendingCameraMoveDispatch = null
|
|
713
|
+
flushPendingCameraMoveEvent()
|
|
714
|
+
}
|
|
715
|
+
pendingCameraMoveDispatch = runnable
|
|
716
|
+
mainHandler.postDelayed(runnable, delay)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
private fun flushPendingCameraMoveEvent(timestamp: Long = SystemClock.uptimeMillis()) {
|
|
720
|
+
val eventData = pendingCameraMoveData ?: return
|
|
721
|
+
pendingCameraMoveData = null
|
|
722
|
+
lastCameraMoveDispatchAt = timestamp
|
|
723
|
+
onCameraMove(eventData)
|
|
724
|
+
}
|
|
725
|
+
|
|
605
726
|
/** 保存实例状态 */
|
|
606
727
|
@Suppress("unused")
|
|
607
728
|
fun onSaveInstanceState(outState: android.os.Bundle) {
|
|
@@ -617,9 +738,10 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
617
738
|
override fun addView(child: View?, index: Int) {
|
|
618
739
|
if (child is MarkerView) {
|
|
619
740
|
child.setMap(aMap)
|
|
620
|
-
// MarkerView
|
|
621
|
-
//
|
|
622
|
-
|
|
741
|
+
// MarkerView 需要保留可测量尺寸,否则 Android 无法正确处理
|
|
742
|
+
// Text / View 的 maxWidth 等布局约束,最终会被测成整行宽度。
|
|
743
|
+
// 这里保留 WRAP_CONTENT,并继续移到屏幕外,避免影响可见布局。
|
|
744
|
+
val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
|
623
745
|
child.layoutParams = params
|
|
624
746
|
child.translationX = -10000f // 移到屏幕外
|
|
625
747
|
child.translationY = -10000f
|
|
@@ -729,7 +851,4 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
729
851
|
return false
|
|
730
852
|
}
|
|
731
853
|
|
|
732
|
-
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
733
|
-
super.onLayout(changed, left, top, right, bottom)
|
|
734
|
-
}
|
|
735
854
|
}
|
|
@@ -3,16 +3,6 @@ package expo.modules.gaodemap
|
|
|
3
3
|
import expo.modules.kotlin.modules.Module
|
|
4
4
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
5
|
|
|
6
|
-
import expo.modules.kotlin.types.Enumerable
|
|
7
|
-
|
|
8
|
-
enum class MapType(val value: Int) : Enumerable {
|
|
9
|
-
STANDARD(1),
|
|
10
|
-
SATELLITE(2),
|
|
11
|
-
NIGHT(3),
|
|
12
|
-
NAVI(4),
|
|
13
|
-
BUS(5)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
6
|
/**
|
|
17
7
|
* 高德地图视图 Module
|
|
18
8
|
*/
|
|
@@ -21,7 +11,7 @@ class ExpoGaodeMapViewModule : Module() {
|
|
|
21
11
|
Name("ExpoGaodeMapView")
|
|
22
12
|
|
|
23
13
|
View(ExpoGaodeMapView::class) {
|
|
24
|
-
Events("onMapPress", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
|
|
14
|
+
Events("onMapPress", "onPressPoi", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
|
|
25
15
|
|
|
26
16
|
|
|
27
17
|
|
|
@@ -34,9 +24,9 @@ class ExpoGaodeMapViewModule : Module() {
|
|
|
34
24
|
}
|
|
35
25
|
|
|
36
26
|
|
|
37
|
-
Prop<
|
|
38
|
-
view.mapType = type
|
|
39
|
-
view.setMapType(type
|
|
27
|
+
Prop<Int>("mapType") { view, type ->
|
|
28
|
+
view.mapType = type
|
|
29
|
+
view.setMapType(type)
|
|
40
30
|
}
|
|
41
31
|
|
|
42
32
|
Prop<Map<String, Any?>?>("initialCameraPosition") { view, position ->
|
|
@@ -64,6 +54,9 @@ class ExpoGaodeMapViewModule : Module() {
|
|
|
64
54
|
Prop<Boolean>("trafficEnabled") { view, show -> view.setShowsTraffic(show) }
|
|
65
55
|
Prop<Boolean>("buildingsEnabled") { view, show -> view.setShowsBuildings(show) }
|
|
66
56
|
Prop<Boolean>("indoorViewEnabled") { view, show -> view.setShowsIndoorMap(show) }
|
|
57
|
+
Prop<Boolean>("labelsEnabled") { view, enabled -> view.setLabelsEnabled(enabled) }
|
|
58
|
+
Prop<Boolean>("myLocationButtonEnabled") { view, enabled -> view.setMyLocationButtonEnabled(enabled) }
|
|
59
|
+
Prop<Int>("cameraEventThrottleMs") { view, throttleMs -> view.setCameraEventThrottleMs(throttleMs) }
|
|
67
60
|
|
|
68
61
|
Prop<Map<String, Any>?>("customMapStyle") { view, styleData ->
|
|
69
62
|
styleData?.let { view.setCustomMapStyle(it) }
|
|
@@ -46,6 +46,13 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
46
46
|
fun setShowsScale(show: Boolean) {
|
|
47
47
|
aMap.uiSettings.isScaleControlsEnabled = show
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 设置是否显示定位按钮
|
|
52
|
+
*/
|
|
53
|
+
fun setMyLocationButtonEnabled(enabled: Boolean) {
|
|
54
|
+
aMap.uiSettings.isMyLocationButtonEnabled = enabled
|
|
55
|
+
}
|
|
49
56
|
|
|
50
57
|
// ==================== 手势控制 ====================
|
|
51
58
|
|
|
@@ -322,7 +329,7 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
322
329
|
fun setShowsTraffic(show: Boolean) {
|
|
323
330
|
aMap.isTrafficEnabled = show
|
|
324
331
|
}
|
|
325
|
-
|
|
332
|
+
|
|
326
333
|
/**
|
|
327
334
|
* 设置是否显示建筑物
|
|
328
335
|
*/
|
|
@@ -336,6 +343,13 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
336
343
|
fun setShowsIndoorMap(show: Boolean) {
|
|
337
344
|
aMap.showIndoorMap(show)
|
|
338
345
|
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 设置是否显示底图文字标注
|
|
349
|
+
*/
|
|
350
|
+
fun setLabelsEnabled(enabled: Boolean) {
|
|
351
|
+
aMap.showMapText(enabled)
|
|
352
|
+
}
|
|
339
353
|
|
|
340
354
|
/**
|
|
341
355
|
* 设置地图类型
|
|
@@ -2,7 +2,6 @@ package expo.modules.gaodemap.overlays
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.util.Log
|
|
6
5
|
import android.graphics.Bitmap
|
|
7
6
|
import android.graphics.BitmapFactory
|
|
8
7
|
import android.graphics.Canvas
|
|
@@ -64,7 +63,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
64
63
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
private var rawPoints: List<Map<String, Any>> = emptyList()
|
|
68
66
|
private var clusterItems: List<ClusterItem> = emptyList()
|
|
69
67
|
private var clusters: List<Cluster> = emptyList()
|
|
70
68
|
|
|
@@ -91,6 +89,7 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
91
89
|
private var currentIconDescriptor: BitmapDescriptor? = null
|
|
92
90
|
private var customIconBitmap: Bitmap? = null
|
|
93
91
|
private var pendingIconUri: String? = null
|
|
92
|
+
private var pendingRetryUpdate: Runnable? = null
|
|
94
93
|
|
|
95
94
|
// 标记样式是否发生变化,用于强制更新图标
|
|
96
95
|
private var styleChanged = false
|
|
@@ -117,7 +116,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
117
116
|
* 设置聚合点
|
|
118
117
|
*/
|
|
119
118
|
fun setPoints(points: List<Map<String, Any>>) {
|
|
120
|
-
rawPoints = points
|
|
121
119
|
clusterItems = points.mapNotNull { pointData ->
|
|
122
120
|
LatLngParser.parseLatLng(pointData)?.let { latLng ->
|
|
123
121
|
ClusterItem(latLng, pointData)
|
|
@@ -141,7 +139,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
141
139
|
* 设置最小聚合数量
|
|
142
140
|
*/
|
|
143
141
|
fun setMinClusterSize(size: Int) {
|
|
144
|
-
Log.d("ClusterView", "setMinClusterSize: $size")
|
|
145
142
|
minClusterSize = size
|
|
146
143
|
updateClusters()
|
|
147
144
|
}
|
|
@@ -227,8 +224,8 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
227
224
|
updateClusters()
|
|
228
225
|
}
|
|
229
226
|
}
|
|
230
|
-
} catch (
|
|
231
|
-
|
|
227
|
+
} catch (_: Exception) {
|
|
228
|
+
// 忽略异常,保留当前默认图标
|
|
232
229
|
}
|
|
233
230
|
}
|
|
234
231
|
}
|
|
@@ -265,6 +262,8 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
265
262
|
}
|
|
266
263
|
currentMarkers.clear()
|
|
267
264
|
bitmapCache.clear()
|
|
265
|
+
pendingRetryUpdate?.let { mainHandler.removeCallbacks(it) }
|
|
266
|
+
pendingRetryUpdate = null
|
|
268
267
|
}
|
|
269
268
|
|
|
270
269
|
/**
|
|
@@ -283,7 +282,12 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
283
282
|
}
|
|
284
283
|
|
|
285
284
|
calculationJob = scope.launch(Dispatchers.Default) {
|
|
286
|
-
if (clusterItems.isEmpty())
|
|
285
|
+
if (clusterItems.isEmpty()) {
|
|
286
|
+
withContext(Dispatchers.Main) {
|
|
287
|
+
renderClusters(emptyList())
|
|
288
|
+
}
|
|
289
|
+
return@launch
|
|
290
|
+
}
|
|
287
291
|
|
|
288
292
|
// 获取当前比例尺 (米/像素)
|
|
289
293
|
val scalePerPixel = withContext(Dispatchers.Main) {
|
|
@@ -296,11 +300,7 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
if (scalePerPixel <= 0) {
|
|
299
|
-
|
|
300
|
-
withContext(Dispatchers.Main) {
|
|
301
|
-
Log.w("ClusterView", "Invalid scalePerPixel: $scalePerPixel, retrying...")
|
|
302
|
-
mainHandler.postDelayed({ updateClusters() }, 500)
|
|
303
|
-
}
|
|
303
|
+
scheduleRetryUpdate()
|
|
304
304
|
return@launch
|
|
305
305
|
}
|
|
306
306
|
|
|
@@ -319,6 +319,18 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
private fun scheduleRetryUpdate(delayMs: Long = 500L) {
|
|
323
|
+
pendingRetryUpdate?.let { mainHandler.removeCallbacks(it) }
|
|
324
|
+
val retryTask = Runnable {
|
|
325
|
+
pendingRetryUpdate = null
|
|
326
|
+
if (aMap != null) {
|
|
327
|
+
updateClusters()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
pendingRetryUpdate = retryTask
|
|
331
|
+
mainHandler.postDelayed(retryTask, delayMs)
|
|
332
|
+
}
|
|
333
|
+
|
|
322
334
|
private fun buildClustersFromNative(radiusMeters: Double): List<Cluster>? {
|
|
323
335
|
return try {
|
|
324
336
|
val latitudes = DoubleArray(clusterItems.size)
|
|
@@ -396,7 +408,6 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
396
408
|
* 使用 Diff 算法优化渲染,避免全量刷新导致的闪烁
|
|
397
409
|
*/
|
|
398
410
|
private fun renderClusters(newClusters: List<Cluster>) {
|
|
399
|
-
Log.d("ClusterView", "renderClusters: count=${newClusters.size}, minClusterSize=$minClusterSize")
|
|
400
411
|
val map = aMap ?: return
|
|
401
412
|
clusters = newClusters
|
|
402
413
|
|