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

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 CHANGED
@@ -44,6 +44,14 @@
44
44
 
45
45
  ## 📦 安装
46
46
 
47
+ > ⚠️ **版本兼容性说明**:
48
+ > - 如果你的项目使用 **Expo SDK 54 及以上**,请安装 默认的 版本。
49
+ > - 如果你的项目使用 **Expo SDK 53 及以下**(如 50, 51, 52, 53),请使用 **V1** 版本(Tag: `v1`)。
50
+ > ```bash
51
+ > npm install expo-gaode-map@v1
52
+ > ```
53
+ > **说明**:V1 版本除了不支持**世界地图**功能外,其余 API 与 V2 (Latest) 版本完全一致。
54
+
47
55
  ### 方案一:仅使用地图和定位(核心包)
48
56
 
49
57
  ```bash
@@ -162,9 +170,46 @@ try {
162
170
  }
163
171
  }
164
172
  ```
173
+ ### 5. 地图在模拟器上显示异常(可能会出现黑屏或闪退)
174
+
175
+ 建议:
176
+ - 确保在真机上测试,模拟器可能不支持地图功能
177
+ - 检查是否正确配置了 API Key
165
178
 
166
179
  **完整错误处理指南**:[ERROR_HANDLING_GUIDE.md](./ERROR_HANDLING_GUIDE.md)
167
180
 
181
+ ### 6. Google Play 版本支持
182
+
183
+ 如果你的应用需要上架 Google Play,需要使用高德地图 Google Play 版本的 SDK(通过了 Google Play 的合规审核)。
184
+
185
+ #### 步骤 1:下载 SDK
186
+ 前往 [高德开放平台](https://lbs.amap.com/api/android-sdk/download) 下载包含 Google Play 版本的 SDK(通常是合包,包含地图、搜索、定位功能)。
187
+
188
+ #### 步骤 2:放入项目
189
+ 将下载的 `.aar` 或 `.jar` 文件放入你的 Expo 项目根目录下的 `libs` 文件夹中(如果没有则创建),例如 `libs/AMap_3DMap_GooglePlay.aar`。
190
+
191
+ #### 步骤 3:配置插件
192
+ 在 `app.json` 中配置 `customMapSdkPath`:
193
+
194
+ ```json
195
+ {
196
+ "expo": {
197
+ "plugins": [
198
+ [
199
+ "expo-gaode-map",
200
+ {
201
+ "androidKey": "your-android-key",
202
+ "iosKey": "your-ios-key",
203
+ "customMapSdkPath": "./libs/AMap_3DMap_GooglePlay.aar"
204
+ }
205
+ ]
206
+ ]
207
+ }
208
+ }
209
+ ```
210
+
211
+ > **注意**:Google Play 版本的 SDK 通常是合包(包含地图+搜索+定位)。如果你的项目中引入了其他高德相关依赖(如 `expo-gaode-map-search`),可能会导致类冲突。插件会自动处理大部分冲突,但如果依然遇到 `Duplicate class` 错误,请检查是否引入了多余的依赖。
212
+
168
213
  支持的错误类型:
169
214
  - `SDK_NOT_INITIALIZED` - SDK 未初始化
170
215
  - `INVALID_API_KEY` - API Key 配置错误
@@ -185,6 +230,7 @@ MIT
185
230
 
186
231
  - [在线文档](https://TomWq.github.io/expo-gaode-map/)
187
232
  - [错误处理指南](./ERROR_HANDLING_GUIDE.md) 🆕
233
+ - [性能优化指南](./PERFORMANCE_GUIDE.md) 🆕
188
234
  - [GitHub 仓库](https://github.com/TomWq/expo-gaode-map)
189
235
  - [示例项目(地图)](https://github.com/TomWq/expo-gaode-map-example)
190
236
  - [示例项目(导航)](https://github.com/TomWq/expo-gaode-map-navigation-example)
@@ -54,5 +54,29 @@ android {
54
54
 
55
55
  dependencies {
56
56
  // 高德地图 3D SDK
57
- implementation ('com.amap.api:3dmap:latest.integration')
57
+ def customSdkPath = null
58
+ println "ExpoGaodeMap: Checking for custom SDK property in rootProject: ${rootProject.name}"
59
+ if (rootProject.hasProperty("EXPO_GAODE_MAP_CUSTOM_SDK_PATH")) {
60
+ customSdkPath = rootProject.property("EXPO_GAODE_MAP_CUSTOM_SDK_PATH")
61
+ println "ExpoGaodeMap: Found custom SDK path property: ${customSdkPath}"
62
+ } else {
63
+ println "ExpoGaodeMap: No custom SDK path property found."
64
+ }
65
+
66
+ if (customSdkPath) {
67
+ def sdkFile = rootProject.file(customSdkPath)
68
+ if (!sdkFile.exists()) {
69
+ // Try to find the file relative to the project root (parent of android folder)
70
+ sdkFile = new File(rootProject.projectDir.parentFile, customSdkPath)
71
+ }
72
+
73
+ if (sdkFile.exists()) {
74
+ println "ExpoGaodeMap: Using custom Map SDK from ${sdkFile.absolutePath}"
75
+ implementation files(sdkFile)
76
+ } else {
77
+ throw new FileNotFoundException("ExpoGaodeMap: Could not find custom SDK at ${customSdkPath}. Please check your customMapSdkPath configuration.")
78
+ }
79
+ } else {
80
+ implementation ('com.amap.api:3dmap:latest.integration')
81
+ }
58
82
  }
@@ -22,3 +22,5 @@ find_library(log-lib log)
22
22
  target_link_libraries(gaodecluster
23
23
  ${log-lib}
24
24
  )
25
+
26
+ target_link_options(gaodecluster PRIVATE "-Wl,-z,max-page-size=16384")
@@ -79,6 +79,9 @@ class ExpoGaodeMapModule : Module() {
79
79
  SDKInitializer.initSDK(appContext.reactContext!!, androidKey)
80
80
  getLocationManager() // 初始化定位管理器
81
81
 
82
+ // 打印当前 SDK 版本信息,便于验证依赖来源
83
+ android.util.Log.i("ExpoGaodeMap", "✅ SDK 初始化完成 - Version: ${MapsInitializer.getVersion()}")
84
+
82
85
  // 初始化成功后自动触发一次预加载
83
86
  MapPreloadManager.startPreload(appContext.reactContext!!, poolSize = 1)
84
87
  } catch (e: SecurityException) {
@@ -446,9 +446,9 @@ class ExpoGaodeMapOfflineModule : Module() {
446
446
  putString("cityName", city.city)
447
447
  putLong("size", city.size)
448
448
  putString("status", status)
449
- putInt("progress", city.getcompleteCode())
449
+ putInt("progress", getDownloadProgress(city))
450
450
  putString("version", city.version)
451
- putLong("downloadedSize", (city.size * city.getcompleteCode() / 100))
451
+ putLong("downloadedSize", (city.size * getDownloadProgress(city) / 100))
452
452
  }
453
453
  }
454
454
 
@@ -461,12 +461,54 @@ class ExpoGaodeMapOfflineModule : Module() {
461
461
  putString("cityName", province.provinceName)
462
462
  putLong("size", province.size)
463
463
  putString("status", getStatusString(province.state))
464
- putInt("progress", province.getcompleteCode())
464
+ putInt("progress", getDownloadProgress(province))
465
465
  putString("version", province.version)
466
466
  putString("provinceName", province.provinceName)
467
467
  putString("provinceCode", province.provinceCode)
468
468
  }
469
469
  }
470
+
471
+ /**
472
+ * 兼容获取 START_DOWNLOAD_FAILED 状态码
473
+ * 国内版拼写为 START_DOWNLOAD_FAILD,Google Play 版修正为 START_DOWNLOAD_FAILED
474
+ */
475
+ private val startDownloadFailedCode: Int by lazy {
476
+ try {
477
+ OfflineMapStatus::class.java.getField("START_DOWNLOAD_FAILED").getInt(null)
478
+ } catch (e: Exception) {
479
+ try {
480
+ OfflineMapStatus::class.java.getField("START_DOWNLOAD_FAILD").getInt(null)
481
+ } catch (e2: Exception) {
482
+ -1
483
+ }
484
+ }
485
+ }
486
+
487
+ /**
488
+ * 兼容获取下载进度
489
+ * Google Play 版本 SDK 可能修复了 getcompleteCode 的命名或使用了不同的 API
490
+ */
491
+ private fun getDownloadProgress(obj: Any): Int {
492
+ try {
493
+ // 尝试标准版的命名 (getcompleteCode)
494
+ val method = obj.javaClass.getMethod("getcompleteCode")
495
+ return method.invoke(obj) as Int
496
+ } catch (e: Exception) {
497
+ try {
498
+ // 尝试修正后的命名 (getCompleteCode) - Google Play 版本可能使用此命名
499
+ val method = obj.javaClass.getMethod("getCompleteCode")
500
+ return method.invoke(obj) as Int
501
+ } catch (e2: Exception) {
502
+ // 如果都失败了,尝试直接访问 completeCode 字段
503
+ try {
504
+ val field = obj.javaClass.getField("completeCode")
505
+ return field.getInt(obj)
506
+ } catch (e3: Exception) {
507
+ return 0
508
+ }
509
+ }
510
+ }
511
+ }
470
512
 
471
513
  /**
472
514
  * 获取状态字符串
@@ -484,7 +526,7 @@ class ExpoGaodeMapOfflineModule : Module() {
484
526
  OfflineMapStatus.EXCEPTION_NETWORK_LOADING -> "downloading" // 网络问题,可继续
485
527
  OfflineMapStatus.EXCEPTION_AMAP -> "failed" // 认证异常
486
528
  OfflineMapStatus.EXCEPTION_SDCARD -> "failed" // SD卡异常
487
- OfflineMapStatus.START_DOWNLOAD_FAILD -> "failed" // 开始下载失败
529
+ startDownloadFailedCode -> "failed" // 兼容两种拼写的开始下载失败
488
530
  OfflineMapStatus.CHECKUPDATES -> "not_downloaded"
489
531
  OfflineMapStatus.NEW_VERSION -> "not_downloaded"
490
532
  else -> "not_downloaded"
@@ -5,7 +5,7 @@ import android.content.Context
5
5
  import android.view.View
6
6
  import android.view.ViewGroup
7
7
  import com.amap.api.maps.AMap
8
- import com.amap.api.maps.MapView
8
+ import com.amap.api.maps.TextureMapView
9
9
  import com.amap.api.maps.MapsInitializer
10
10
  import com.amap.api.maps.model.LatLng
11
11
  import expo.modules.kotlin.AppContext
@@ -64,23 +64,11 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
64
64
  private val onCameraMove by EventDispatcher()
65
65
  private val onCameraIdle by EventDispatcher()
66
66
 
67
- // 事件节流控制
68
- /** 相机移动事件节流间隔(毫秒) */
69
- private val CAMERA_MOVE_THROTTLE_MS = 100L
70
- /** 上次触发相机移动事件的时间戳 */
71
- private var lastCameraMoveTime = 0L
72
- /** 缓存的相机移动事件数据 */
67
+ // 缓存的相机移动事件数据
73
68
  private var pendingCameraMoveData: Map<String, Any>? = null
74
- /** 节流定时器 Runnable */
75
- private val throttleRunnable = Runnable {
76
- pendingCameraMoveData?.let { data ->
77
- onCameraMove(data)
78
- pendingCameraMoveData = null
79
- }
80
- }
81
69
 
82
70
  // 高德地图视图
83
- private lateinit var mapView: MapView
71
+ private lateinit var mapView: TextureMapView
84
72
  private lateinit var aMap: AMap
85
73
 
86
74
  // 管理器
@@ -97,18 +85,9 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
97
85
  MapsInitializer.updatePrivacyShow(context, true, true)
98
86
  MapsInitializer.updatePrivacyAgree(context, true)
99
87
 
100
- // 尝试从预加载池获取 MapView
101
- val preloadedMapView = MapPreloadManager.getPreloadedMapView()
102
-
103
- if (preloadedMapView != null) {
104
- mapView = preloadedMapView
105
- android.util.Log.i("ExpoGaodeMapView", "🚀 使用预加载的 MapView 实例")
106
- } else {
107
- // 创建地图视图
108
- mapView = MapView(context)
109
- mapView.onCreate(null)
110
- android.util.Log.i("ExpoGaodeMapView", "⚠️ 创建新的 MapView 实例 (未命中预加载池)")
111
- }
88
+ // 创建地图视图 - 使用 TextureMapView 以支持截图
89
+ mapView = TextureMapView(context)
90
+ mapView.onCreate(null)
112
91
 
113
92
  aMap = mapView.map
114
93
 
@@ -184,7 +163,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
184
163
 
185
164
  // 相机移动中 - 应用节流优化
186
165
  cameraPosition?.let {
187
- val currentTime = System.currentTimeMillis()
188
166
  val visibleRegion = aMap.projection.visibleRegion
189
167
  val eventData = mapOf(
190
168
  "cameraPosition" to mapOf(
@@ -208,23 +186,16 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
208
186
  )
209
187
  )
210
188
 
211
- // 节流逻辑:100ms 内只触发一次
212
- if (currentTime - lastCameraMoveTime >= CAMERA_MOVE_THROTTLE_MS) {
213
- // 超过节流时间,立即触发事件
214
- lastCameraMoveTime = currentTime
215
- onCameraMove(eventData)
216
- // 清除待处理的事件和定时器
217
- mainHandler.removeCallbacks(throttleRunnable)
218
- pendingCameraMoveData = null
219
- } else {
220
- // 在节流时间内,缓存事件数据,使用定时器延迟触发
221
- pendingCameraMoveData = eventData
222
- mainHandler.removeCallbacks(throttleRunnable)
223
- mainHandler.postDelayed(
224
- throttleRunnable,
225
- CAMERA_MOVE_THROTTLE_MS - (currentTime - lastCameraMoveTime)
226
- )
227
- }
189
+ // 使用 onCameraMove 自身的节流机制(如果在 Module 定义中配置了 Coalescing)
190
+ // 或者在这里简单发送,让 JS 端处理节流,或者依赖 Expo 的事件批处理
191
+ // 这里我们移除自定义的 Handler 实现,直接发送事件,简化代码逻辑
192
+ // 注意:高德地图的 onCameraChange 调用频率非常高,
193
+ // 建议在 Module 定义中使用 Events("onCameraMove") 时考虑是否需要原生侧节流
194
+ // 目前 Expo Modules 默认没有自动节流,但为了代码简洁和避免 Handler 泄漏风险,
195
+ // 我们可以依赖 JS 端的 debounce/throttle,或者如果性能是瓶颈,再加回轻量级的节流。
196
+ // 鉴于之前的 Handler 实现比较复杂且容易出错,我们先简化。
197
+
198
+ onCameraMove(eventData)
228
199
  }
229
200
  }
230
201
 
@@ -493,27 +464,34 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
493
464
 
494
465
  aMap.getMapScreenShot(object : AMap.OnMapScreenShotListener {
495
466
  override fun onMapScreenShot(bitmap: android.graphics.Bitmap?) {
496
- // 如果已经处理过,直接返回
467
+ // 这个回调通常在旧版 SDK 或部分机型触发
468
+ // 如果已经处理过(通过带 status 的回调),则忽略
497
469
  if (isSettled.getAndSet(true)) return
498
470
 
499
- // 旧版本回调,为了兼容性也处理
500
- bitmap?.let { handleSnapshot(it, promise) } ?: run {
501
- promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
471
+ if (bitmap == null) {
472
+ promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
473
+ return
502
474
  }
475
+ handleSnapshot(bitmap, promise)
503
476
  }
504
477
 
505
478
  override fun onMapScreenShot(bitmap: android.graphics.Bitmap?, status: Int) {
506
479
  // 如果已经处理过,直接返回
507
480
  if (isSettled.getAndSet(true)) return
508
481
 
509
- // status != 0 表示失败
510
- if (status != 0) {
511
- promise.reject("SNAPSHOT_FAILED", "Failed to take snapshot, status code: $status", null)
482
+ if (bitmap == null) {
483
+ promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
512
484
  return
513
485
  }
514
- bitmap?.let { handleSnapshot(it, promise) } ?: run {
515
- promise.reject("SNAPSHOT_FAILED", "Bitmap is null", null)
486
+
487
+ // 根据高德文档:
488
+ // status != 0 地图渲染完成,截屏无网格
489
+ // status == 0 地图未渲染完成,截屏有网格
490
+ if (status == 0) {
491
+ android.util.Log.w("ExpoGaodeMapView", "Warning: Map snapshot taken before rendering completed (grid may be visible)")
516
492
  }
493
+
494
+ handleSnapshot(bitmap, promise)
517
495
  }
518
496
  })
519
497
  }
@@ -601,10 +579,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
601
579
  @Suppress("unused")
602
580
  fun onDestroy() {
603
581
  try {
604
- // 清理节流定时器
605
- mainHandler.removeCallbacks(throttleRunnable)
606
- pendingCameraMoveData = null
607
-
608
582
  // 清理 Handler 回调,防止内存泄露
609
583
  mainHandler.removeCallbacksAndMessages(null)
610
584
 
@@ -654,7 +628,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
654
628
  return
655
629
  }
656
630
 
657
- if (child is MapView) {
631
+ if (child is TextureMapView) {
658
632
  super.addView(child, index)
659
633
  return
660
634
  }
@@ -702,7 +676,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
702
676
  try {
703
677
  val child = super.getChildAt(index)
704
678
 
705
- if (child is MapView) {
679
+ if (child is TextureMapView) {
706
680
  return
707
681
  }
708
682
 
@@ -3,6 +3,16 @@ 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
+
6
16
  /**
7
17
  * 高德地图视图 Module
8
18
  */
@@ -24,9 +34,9 @@ class ExpoGaodeMapViewModule : Module() {
24
34
  }
25
35
 
26
36
 
27
- Prop<Int>("mapType") { view, type ->
28
- view.mapType = type
29
- view.setMapType(type)
37
+ Prop<MapType>("mapType") { view, type ->
38
+ view.mapType = type.value
39
+ view.setMapType(type.value)
30
40
  }
31
41
 
32
42
  Prop<Map<String, Any?>?>("initialCameraPosition") { view, position ->
package/build/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { PlatformDetector, DeviceType, FoldState, isAndroid14Plus, isiOS17Plus,
10
10
  export type { DeviceInfo, SystemVersion } from './utils/PlatformDetector';
11
11
  export { PermissionUtils, PermissionManager, // 向后兼容的别名
12
12
  LocationPermissionType, } from './utils/PermissionUtils';
13
+ export { throttle } from './utils/throttle';
13
14
  export { FoldableMapView, useFoldableMap, } from './components/FoldableMapView';
14
15
  export type { FoldableMapViewProps, FoldableConfig, } from './components/FoldableMapView';
15
16
  export { default as ExpoGaodeMapOfflineModule } from './utils/OfflineMapManager';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,GACR,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,EACV,MAAM,GACP,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EACL,eAAe,EACf,iBAAiB,EAAE,UAAU;AAC7B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,EACpB,cAAc,GACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEjF,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAM/B;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,wOAGjC,CAAA;AAGF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG/D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,GACR,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,EACV,MAAM,GACP,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EACL,eAAe,EACf,iBAAiB,EAAE,UAAU;AAC7B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,EACpB,cAAc,GACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEjF,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAM/B;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,wOAGjC,CAAA;AAGF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG/D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
package/build/index.js CHANGED
@@ -17,6 +17,8 @@ export { PlatformDetector, DeviceType, FoldState, isAndroid14Plus, isiOS17Plus,
17
17
  // 导出权限工具类(仅提供文案和诊断,实际权限请求使用 ExpoGaodeMapModule)
18
18
  export { PermissionUtils, PermissionManager, // 向后兼容的别名
19
19
  LocationPermissionType, } from './utils/PermissionUtils';
20
+ // 导出节流工具函数
21
+ export { throttle } from './utils/throttle';
20
22
  // 导出折叠屏适配组件
21
23
  export { FoldableMapView, useFoldableMap, } from './components/FoldableMapView';
22
24
  // 导出离线地图 API
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,6BAA6B,MAAM,sBAAsB,CAAC;AAEjE,mBAAmB;AACnB,cAAc,SAAS,CAAC;AACxB,SAAS;AACT,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAErE,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,UAAU;AACV,OAAO,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,GACR,MAAM,uBAAuB,CAAC;AAE/B,WAAW;AACX,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,sBAAsB,CAAC;AAG9B,WAAW;AACX,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,EACV,MAAM,GACP,MAAM,0BAA0B,CAAC;AAGlC,gDAAgD;AAChD,OAAO,EACL,eAAe,EACf,iBAAiB,EAAE,UAAU;AAC7B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,YAAY;AACZ,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,8BAA8B,CAAC;AAMtC,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAejF,MAAM,uBAAuB,GAAG,6BAA6B,CAAC,yBAAyB,CAAA;AACvF,MAAM,mBAAmB,GAAG,6BAA6B,CAAC,uBAAuB,CAAA;AAGjF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;IACzD,SAAS,EAAE,mBAAmB;IAC9B,aAAa,EAAE,uBAAuB;CACvC,CAAC,CAAA;AAEF,yBAAyB;AACzB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D,WAAW;AACX,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["\nimport { createPermissionHook } from 'expo-modules-core';\nimport ExpoGaodeMapModuleWithHelpers from './ExpoGaodeMapModule';\n\n// 导出类型定义(包含所有通用类型)\nexport * from './types';\n// 导出原生模块\nexport { default as ExpoGaodeMapModule } from './ExpoGaodeMapModule';\n\n// 导出地图视图组件\nexport { default as MapView } from './ExpoGaodeMapView';\nexport { useMap } from './components/MapContext';\nexport { MapUI } from './components/MapUI';\n\n// 导出覆盖物组件\nexport {\n Marker,\n Polyline,\n Polygon,\n Circle,\n HeatMap,\n MultiPoint,\n Cluster,\n} from './components/overlays';\n\n// 导出错误处理工具\nexport {\n ErrorHandler,\n ErrorLogger,\n GaodeMapError,\n ErrorType,\n} from './utils/ErrorHandler';\nexport type { ErrorDetails } from './utils/ErrorHandler';\n\n// 导出平台检测工具\nexport {\n PlatformDetector,\n DeviceType,\n FoldState,\n isAndroid14Plus,\n isiOS17Plus,\n isTablet,\n isFoldable,\n isIPad,\n} from './utils/PlatformDetector';\nexport type { DeviceInfo, SystemVersion } from './utils/PlatformDetector';\n\n// 导出权限工具类(仅提供文案和诊断,实际权限请求使用 ExpoGaodeMapModule)\nexport {\n PermissionUtils,\n PermissionManager, // 向后兼容的别名\n LocationPermissionType,\n} from './utils/PermissionUtils';\n\n// 导出折叠屏适配组件\nexport {\n FoldableMapView,\n useFoldableMap,\n} from './components/FoldableMapView';\nexport type {\n FoldableMapViewProps,\n FoldableConfig,\n} from './components/FoldableMapView';\n\n// 导出离线地图 API\nexport { default as ExpoGaodeMapOfflineModule } from './utils/OfflineMapManager';\n\nexport type {\n OfflineMapInfo,\n OfflineMapStatus,\n OfflineMapDownloadConfig,\n OfflineMapDownloadEvent,\n OfflineMapCompleteEvent,\n OfflineMapErrorEvent,\n OfflineMapPausedEvent,\n OfflineMapCancelledEvent,\n OfflineMapStorageInfo,\n OfflineMapEvents,\n} from './types/offline.types';\n\nconst requestPermissionsAsync = ExpoGaodeMapModuleWithHelpers.requestLocationPermission\nconst getPermissionsAsync = ExpoGaodeMapModuleWithHelpers.checkLocationPermission\n\n\n/**\n * Check or request permissions to access the location.\n * This uses both `requestPermissionsAsync` and `getPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = useLocationPermissions();\n * ```\n */\nexport const useLocationPermissions = createPermissionHook({\n getMethod: getPermissionsAsync,\n requestMethod: requestPermissionsAsync,\n})\n\n// 导出便捷读取的 SDK 配置与 webKey\nexport { getSDKConfig, getWebKey } from './ExpoGaodeMapModule';\n\n// 默认导出原生模块\nexport { default } from './ExpoGaodeMapModule';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,6BAA6B,MAAM,sBAAsB,CAAC;AAEjE,mBAAmB;AACnB,cAAc,SAAS,CAAC;AACxB,SAAS;AACT,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAErE,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,UAAU;AACV,OAAO,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,GACR,MAAM,uBAAuB,CAAC;AAE/B,WAAW;AACX,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,sBAAsB,CAAC;AAG9B,WAAW;AACX,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,EACV,MAAM,GACP,MAAM,0BAA0B,CAAC;AAGlC,gDAAgD;AAChD,OAAO,EACL,eAAe,EACf,iBAAiB,EAAE,UAAU;AAC7B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,WAAW;AACX,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,YAAY;AACZ,OAAO,EACL,eAAe,EACf,cAAc,GACf,MAAM,8BAA8B,CAAC;AAMtC,aAAa;AACb,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAejF,MAAM,uBAAuB,GAAG,6BAA6B,CAAC,yBAAyB,CAAA;AACvF,MAAM,mBAAmB,GAAG,6BAA6B,CAAC,uBAAuB,CAAA;AAGjF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;IACzD,SAAS,EAAE,mBAAmB;IAC9B,aAAa,EAAE,uBAAuB;CACvC,CAAC,CAAA;AAEF,yBAAyB;AACzB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D,WAAW;AACX,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["\nimport { createPermissionHook } from 'expo-modules-core';\nimport ExpoGaodeMapModuleWithHelpers from './ExpoGaodeMapModule';\n\n// 导出类型定义(包含所有通用类型)\nexport * from './types';\n// 导出原生模块\nexport { default as ExpoGaodeMapModule } from './ExpoGaodeMapModule';\n\n// 导出地图视图组件\nexport { default as MapView } from './ExpoGaodeMapView';\nexport { useMap } from './components/MapContext';\nexport { MapUI } from './components/MapUI';\n\n// 导出覆盖物组件\nexport {\n Marker,\n Polyline,\n Polygon,\n Circle,\n HeatMap,\n MultiPoint,\n Cluster,\n} from './components/overlays';\n\n// 导出错误处理工具\nexport {\n ErrorHandler,\n ErrorLogger,\n GaodeMapError,\n ErrorType,\n} from './utils/ErrorHandler';\nexport type { ErrorDetails } from './utils/ErrorHandler';\n\n// 导出平台检测工具\nexport {\n PlatformDetector,\n DeviceType,\n FoldState,\n isAndroid14Plus,\n isiOS17Plus,\n isTablet,\n isFoldable,\n isIPad,\n} from './utils/PlatformDetector';\nexport type { DeviceInfo, SystemVersion } from './utils/PlatformDetector';\n\n// 导出权限工具类(仅提供文案和诊断,实际权限请求使用 ExpoGaodeMapModule)\nexport {\n PermissionUtils,\n PermissionManager, // 向后兼容的别名\n LocationPermissionType,\n} from './utils/PermissionUtils';\n\n// 导出节流工具函数\nexport { throttle } from './utils/throttle';\n\n// 导出折叠屏适配组件\nexport {\n FoldableMapView,\n useFoldableMap,\n} from './components/FoldableMapView';\nexport type {\n FoldableMapViewProps,\n FoldableConfig,\n} from './components/FoldableMapView';\n\n// 导出离线地图 API\nexport { default as ExpoGaodeMapOfflineModule } from './utils/OfflineMapManager';\n\nexport type {\n OfflineMapInfo,\n OfflineMapStatus,\n OfflineMapDownloadConfig,\n OfflineMapDownloadEvent,\n OfflineMapCompleteEvent,\n OfflineMapErrorEvent,\n OfflineMapPausedEvent,\n OfflineMapCancelledEvent,\n OfflineMapStorageInfo,\n OfflineMapEvents,\n} from './types/offline.types';\n\nconst requestPermissionsAsync = ExpoGaodeMapModuleWithHelpers.requestLocationPermission\nconst getPermissionsAsync = ExpoGaodeMapModuleWithHelpers.checkLocationPermission\n\n\n/**\n * Check or request permissions to access the location.\n * This uses both `requestPermissionsAsync` and `getPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = useLocationPermissions();\n * ```\n */\nexport const useLocationPermissions = createPermissionHook({\n getMethod: getPermissionsAsync,\n requestMethod: requestPermissionsAsync,\n})\n\n// 导出便捷读取的 SDK 配置与 webKey\nexport { getSDKConfig, getWebKey } from './ExpoGaodeMapModule';\n\n// 默认导出原生模块\nexport { default } from './ExpoGaodeMapModule';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"GeoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/GeoUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAuB1D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE,CAAC;AACrE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC;AACzE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC"}
1
+ {"version":3,"file":"GeoUtils.d.ts","sourceRoot":"","sources":["../../src/utils/GeoUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE5D;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA+B1D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE,CAAC;AACrE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC;AACzE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC"}
@@ -8,8 +8,8 @@
8
8
  */
9
9
  export function normalizeLatLng(point) {
10
10
  if (Array.isArray(point)) {
11
- let longitude = point[0];
12
- let latitude = point[1];
11
+ let longitude = Number(point[0]);
12
+ let latitude = Number(point[1]);
13
13
  // 智能纠错:如果纬度超出范围 [-90, 90] 且交换后在范围内,则认为是用户传反了
14
14
  if (Math.abs(latitude) > 90 && Math.abs(longitude) <= 90) {
15
15
  console.warn(`[expo-gaode-map] 检测到坐标数组格式可能为 [latitude, longitude] (${point}),已自动纠正为 [longitude, latitude]。建议显式使用 [经度, 纬度] 格式以遵循 GeoJSON 标准。`);
@@ -23,7 +23,14 @@ export function normalizeLatLng(point) {
23
23
  latitude,
24
24
  };
25
25
  }
26
- // 对象格式直接返回
26
+ // 对象格式:强制转换为数字,防止传入 string 类型导致原生层 Crash
27
+ if (point && typeof point === 'object') {
28
+ const p = point;
29
+ return {
30
+ latitude: Number(p.latitude),
31
+ longitude: Number(p.longitude),
32
+ };
33
+ }
27
34
  return point;
28
35
  }
29
36
  export function normalizeLatLngList(points) {
@@ -1 +1 @@
1
- {"version":3,"file":"GeoUtils.js","sourceRoot":"","sources":["../../src/utils/GeoUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAExB,6CAA6C;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YACzD,OAAO,CAAC,IAAI,CACV,wDAAwD,KAAK,kEAAkE,CAChI,CAAC;YACF,OAAO;gBACL,QAAQ,EAAE,SAAS;gBACnB,SAAS,EAAE,QAAQ;aACpB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,SAAS;YACT,QAAQ;SACT,CAAC;IACJ,CAAC;IACD,WAAW;IACX,OAAO,KAAK,CAAC;AACf,CAAC;AAYD,MAAM,UAAU,mBAAmB,CAAC,MAAW;IAC7C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9C,oDAAoD;IACpD,8CAA8C;IAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,yDAAyD;QACzD,0CAA0C;QAC1C,8BAA8B;QAE9B,0CAA0C;QAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7H,OAAQ,MAA0B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,8DAA8D;QAC9D,mCAAmC;QACnC,0BAA0B;QAC1B,8BAA8B;QAC9B,8BAA8B;QAE9B,qCAAqC;QACrC,sCAAsC;QACtC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACvB,OAAQ,MAAwB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,OAAQ,MAA0B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAQ,MAAwB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,IAAa;IAC5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,6BAA6B;IAC7B,IAAI,UAAU,IAAI,IAAI,IAAI,WAAW,IAAK,IAAgC;QAAE,OAAO,IAAI,CAAC;IACxF,2BAA2B;IAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACvH,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { LatLng, LatLngPoint } from '../types/common.types';\n\n/**\n * 将坐标点归一化为对象格式\n * 支持 [longitude, latitude] 数组或 { latitude, longitude } 对象\n * 注意:数组格式遵循 GeoJSON 标准,即 [经度, 纬度]\n * \n * @param point 坐标点\n * @returns { latitude, longitude } 对象\n */\nexport function normalizeLatLng(point: LatLngPoint): LatLng {\n if (Array.isArray(point)) {\n let longitude = point[0];\n let latitude = point[1];\n\n // 智能纠错:如果纬度超出范围 [-90, 90] 且交换后在范围内,则认为是用户传反了\n if (Math.abs(latitude) > 90 && Math.abs(longitude) <= 90) {\n console.warn(\n `[expo-gaode-map] 检测到坐标数组格式可能为 [latitude, longitude] (${point}),已自动纠正为 [longitude, latitude]。建议显式使用 [经度, 纬度] 格式以遵循 GeoJSON 标准。`\n );\n return {\n latitude: longitude,\n longitude: latitude\n };\n }\n\n return {\n longitude,\n latitude,\n };\n }\n // 对象格式直接返回\n return point;\n}\n\n/**\n * 将坐标点数组归一化为对象数组\n * 支持一维数组 [p1, p2, ...] 或二维嵌套数组 [[p1, p2, ...], [p3, p4, ...]]\n * \n * @param points 坐标点数组或嵌套数组\n * @returns 归一化后的坐标数组\n */\nexport function normalizeLatLngList(points: LatLngPoint[]): LatLng[];\nexport function normalizeLatLngList(points: LatLngPoint[][]): LatLng[][];\nexport function normalizeLatLngList(points: LatLngPoint[] | LatLngPoint[][]): LatLng[] | LatLng[][];\nexport function normalizeLatLngList(points: any): any {\n if (!points || points.length === 0) return [];\n\n // 检查是否为嵌套数组 (检查第一项是否也是数组或对象,且不符合 LatLngPoint 的基本判断)\n // 如果第一项是数组且长度 > 2,或者第一项的第一个元素也是数组/对象,则认为是嵌套数组\n const firstItem = points[0];\n \n if (Array.isArray(firstItem)) {\n // 如果第一项是 [number, number],则这可能是一个一维坐标点数组 (LatLngPoint[])\n // 除非所有元素都是数组且至少有一个子数组长度不等于2,或者我们明确知道这是嵌套的\n // 为了支持 Polygon 的嵌套格式,我们需要递归处理\n \n // 启发式判断:如果第一项是数组且它的第一个元素也是数组或对象,那么它一定是嵌套的\n if (Array.isArray(firstItem[0]) || (typeof firstItem[0] === 'object' && firstItem[0] !== null && 'latitude' in firstItem[0])) {\n return (points as LatLngPoint[][]).map(ring => ring.map(normalizeLatLng));\n }\n \n // 另一种情况:用户传入的是 [[lng, lat], [lng, lat]],这既可以看作 LatLngPoint[],\n // 也可以看作 LatLngPoint[][] (只有一个外轮廓)。\n // 在 Polygon 组件中,我们希望统一处理。\n // 如果是 Polygon 传入的,我们希望保留嵌套结构。\n // 但 normalizeLatLngList 是通用的。\n \n // 改进逻辑:如果 data[0] 是一个点,我们把它当作一维数组处理。\n // 如果 data[0] 不是一个点(即它是点的集合),我们当作二维处理。\n if (isPoint(firstItem)) {\n return (points as LatLngPoint[]).map(normalizeLatLng);\n } else {\n return (points as LatLngPoint[][]).map(ring => ring.map(normalizeLatLng));\n }\n }\n\n return (points as LatLngPoint[]).map(normalizeLatLng);\n}\n\n/**\n * 判断一个对象是否为坐标点 (LatLngPoint)\n */\nfunction isPoint(item: unknown): item is LatLngPoint {\n if (!item || typeof item !== 'object') return false;\n // { latitude, longitude } 格式\n if ('latitude' in item && 'longitude' in (item as Record<string, unknown>)) return true;\n // [longitude, latitude] 格式\n if (Array.isArray(item) && item.length >= 2 && typeof item[0] === 'number' && typeof item[1] === 'number') return true;\n return false;\n}\n"]}
1
+ {"version":3,"file":"GeoUtils.js","sourceRoot":"","sources":["../../src/utils/GeoUtils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhC,6CAA6C;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YACzD,OAAO,CAAC,IAAI,CACV,wDAAwD,KAAK,kEAAkE,CAChI,CAAC;YACF,OAAO;gBACL,QAAQ,EAAE,SAAS;gBACnB,SAAS,EAAE,QAAQ;aACpB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,SAAS;YACT,QAAQ;SACT,CAAC;IACJ,CAAC;IACD,yCAAyC;IACzC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAY,CAAC;QACvB,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC5B,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,KAAe,CAAC;AACzB,CAAC;AAYD,MAAM,UAAU,mBAAmB,CAAC,MAAW;IAC7C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE9C,oDAAoD;IACpD,8CAA8C;IAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,yDAAyD;QACzD,0CAA0C;QAC1C,8BAA8B;QAE9B,0CAA0C;QAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7H,OAAQ,MAA0B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,8DAA8D;QAC9D,mCAAmC;QACnC,0BAA0B;QAC1B,8BAA8B;QAC9B,8BAA8B;QAE9B,qCAAqC;QACrC,sCAAsC;QACtC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACvB,OAAQ,MAAwB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,OAAQ,MAA0B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAQ,MAAwB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,IAAa;IAC5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,6BAA6B;IAC7B,IAAI,UAAU,IAAI,IAAI,IAAI,WAAW,IAAK,IAAgC;QAAE,OAAO,IAAI,CAAC;IACxF,2BAA2B;IAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACvH,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { LatLng, LatLngPoint } from '../types/common.types';\n\n/**\n * 将坐标点归一化为对象格式\n * 支持 [longitude, latitude] 数组或 { latitude, longitude } 对象\n * 注意:数组格式遵循 GeoJSON 标准,即 [经度, 纬度]\n * \n * @param point 坐标点\n * @returns { latitude, longitude } 对象\n */\nexport function normalizeLatLng(point: LatLngPoint): LatLng {\n if (Array.isArray(point)) {\n let longitude = Number(point[0]);\n let latitude = Number(point[1]);\n\n // 智能纠错:如果纬度超出范围 [-90, 90] 且交换后在范围内,则认为是用户传反了\n if (Math.abs(latitude) > 90 && Math.abs(longitude) <= 90) {\n console.warn(\n `[expo-gaode-map] 检测到坐标数组格式可能为 [latitude, longitude] (${point}),已自动纠正为 [longitude, latitude]。建议显式使用 [经度, 纬度] 格式以遵循 GeoJSON 标准。`\n );\n return {\n latitude: longitude,\n longitude: latitude\n };\n }\n\n return {\n longitude,\n latitude,\n };\n }\n // 对象格式:强制转换为数字,防止传入 string 类型导致原生层 Crash\n if (point && typeof point === 'object') {\n const p = point as any;\n return {\n latitude: Number(p.latitude),\n longitude: Number(p.longitude),\n };\n }\n \n return point as LatLng;\n}\n\n/**\n * 将坐标点数组归一化为对象数组\n * 支持一维数组 [p1, p2, ...] 或二维嵌套数组 [[p1, p2, ...], [p3, p4, ...]]\n * \n * @param points 坐标点数组或嵌套数组\n * @returns 归一化后的坐标数组\n */\nexport function normalizeLatLngList(points: LatLngPoint[]): LatLng[];\nexport function normalizeLatLngList(points: LatLngPoint[][]): LatLng[][];\nexport function normalizeLatLngList(points: LatLngPoint[] | LatLngPoint[][]): LatLng[] | LatLng[][];\nexport function normalizeLatLngList(points: any): any {\n if (!points || points.length === 0) return [];\n\n // 检查是否为嵌套数组 (检查第一项是否也是数组或对象,且不符合 LatLngPoint 的基本判断)\n // 如果第一项是数组且长度 > 2,或者第一项的第一个元素也是数组/对象,则认为是嵌套数组\n const firstItem = points[0];\n \n if (Array.isArray(firstItem)) {\n // 如果第一项是 [number, number],则这可能是一个一维坐标点数组 (LatLngPoint[])\n // 除非所有元素都是数组且至少有一个子数组长度不等于2,或者我们明确知道这是嵌套的\n // 为了支持 Polygon 的嵌套格式,我们需要递归处理\n \n // 启发式判断:如果第一项是数组且它的第一个元素也是数组或对象,那么它一定是嵌套的\n if (Array.isArray(firstItem[0]) || (typeof firstItem[0] === 'object' && firstItem[0] !== null && 'latitude' in firstItem[0])) {\n return (points as LatLngPoint[][]).map(ring => ring.map(normalizeLatLng));\n }\n \n // 另一种情况:用户传入的是 [[lng, lat], [lng, lat]],这既可以看作 LatLngPoint[],\n // 也可以看作 LatLngPoint[][] (只有一个外轮廓)。\n // 在 Polygon 组件中,我们希望统一处理。\n // 如果是 Polygon 传入的,我们希望保留嵌套结构。\n // 但 normalizeLatLngList 是通用的。\n \n // 改进逻辑:如果 data[0] 是一个点,我们把它当作一维数组处理。\n // 如果 data[0] 不是一个点(即它是点的集合),我们当作二维处理。\n if (isPoint(firstItem)) {\n return (points as LatLngPoint[]).map(normalizeLatLng);\n } else {\n return (points as LatLngPoint[][]).map(ring => ring.map(normalizeLatLng));\n }\n }\n\n return (points as LatLngPoint[]).map(normalizeLatLng);\n}\n\n/**\n * 判断一个对象是否为坐标点 (LatLngPoint)\n */\nfunction isPoint(item: unknown): item is LatLngPoint {\n if (!item || typeof item !== 'object') return false;\n // { latitude, longitude } 格式\n if ('latitude' in item && 'longitude' in (item as Record<string, unknown>)) return true;\n // [longitude, latitude] 格式\n if (Array.isArray(item) && item.length >= 2 && typeof item[0] === 'number' && typeof item[1] === 'number') return true;\n return false;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 一个轻量级的节流函数,用于限制函数调用的频率。
3
+ * 特别适用于处理高频事件,如地图移动、滚动等。
4
+ *
5
+ * @param func 需要节流的函数
6
+ * @param limit 时间间隔(毫秒)
7
+ * @returns 节流后的函数
8
+ */
9
+ export declare function throttle<T extends (...args: any[]) => void>(func: T, limit: number): T;
10
+ //# sourceMappingURL=throttle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../src/utils/throttle.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAStF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 一个轻量级的节流函数,用于限制函数调用的频率。
3
+ * 特别适用于处理高频事件,如地图移动、滚动等。
4
+ *
5
+ * @param func 需要节流的函数
6
+ * @param limit 时间间隔(毫秒)
7
+ * @returns 节流后的函数
8
+ */
9
+ export function throttle(func, limit) {
10
+ let inThrottle;
11
+ return function (...args) {
12
+ if (!inThrottle) {
13
+ func.apply(this, args);
14
+ inThrottle = true;
15
+ setTimeout(() => (inThrottle = false), limit);
16
+ }
17
+ };
18
+ }
19
+ //# sourceMappingURL=throttle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throttle.js","sourceRoot":"","sources":["../../src/utils/throttle.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAqC,IAAO,EAAE,KAAa;IACjF,IAAI,UAAmB,CAAC;IACxB,OAAO,UAAoB,GAAG,IAAW;QACvC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACvB,UAAU,GAAG,IAAI,CAAC;YAClB,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAM,CAAC;AACT,CAAC","sourcesContent":["\n/**\n * 一个轻量级的节流函数,用于限制函数调用的频率。\n * 特别适用于处理高频事件,如地图移动、滚动等。\n *\n * @param func 需要节流的函数\n * @param limit 时间间隔(毫秒)\n * @returns 节流后的函数\n */\nexport function throttle<T extends (...args: any[]) => void>(func: T, limit: number): T {\n let inThrottle: boolean;\n return function(this: any, ...args: any[]) {\n if (!inThrottle) {\n func.apply(this, args);\n inThrottle = true;\n setTimeout(() => (inThrottle = false), limit);\n }\n } as T;\n}\n"]}
@@ -19,11 +19,9 @@ Pod::Spec.new do |s|
19
19
  s.static_framework = true
20
20
 
21
21
  s.dependency 'ExpoModulesCore'
22
- s.dependency 'AMapFoundation'
23
- s.dependency 'AMapLocation'
24
22
  s.dependency 'AMap3DMap'
25
-
26
- s.library = 'c++'
23
+ s.dependency "AMapLocation"
24
+ s.dependency "AMapLocation"
27
25
 
28
26
  # Swift/Objective-C compatibility
29
27
  s.pod_target_xcconfig = {
@@ -124,7 +124,7 @@ public class ExpoGaodeMapModule: Module {
124
124
  * @param enable 是否开启
125
125
  */
126
126
  Function("setLoadWorldVectorMap") { (enable: Bool) in
127
- MAMapView.loadWorldVectorMap = enable
127
+ MAMapView.loadWorldVectorMap = enable
128
128
  }
129
129
 
130
130
  /**
@@ -92,14 +92,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
92
92
 
93
93
  // MARK: - 事件节流控制
94
94
 
95
- /// 相机移动事件节流间隔(秒)
96
- private let cameraMoveThrottleInterval: TimeInterval = 0.1
97
- /// 上次触发相机移动事件的时间戳
98
- private var lastCameraMoveTime: TimeInterval = 0
99
95
  /// 缓存的相机移动事件数据
100
96
  private var pendingCameraMoveData: [String: Any]?
101
- /// 节流定时器
102
- private var throttleTimer: Timer?
103
97
 
104
98
  /// 缩放手势识别器(用于模拟惯性)
105
99
  private var pinchGesture: UIPinchGestureRecognizer!
@@ -856,9 +850,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
856
850
  * 当视图从层级中移除并释放时自动调用
857
851
  */
858
852
  deinit {
859
- // 清理节流定时器
860
- throttleTimer?.invalidate()
861
- throttleTimer = nil
853
+ // 清理资源
862
854
  pendingCameraMoveData = nil
863
855
 
864
856
  // 清理代理,停止接收回调
@@ -935,11 +927,10 @@ extension ExpoGaodeMapView {
935
927
  }
936
928
 
937
929
  /**
938
- * 地图区域即将改变时触发 - 应用节流优化
930
+ * 地图区域即将改变时触发
939
931
  */
940
932
  public func mapView(_ mapView: MAMapView, regionWillChangeAnimated animated: Bool) {
941
- // 相机开始移动 - 应用节流优化
942
- let currentTime = Date().timeIntervalSince1970
933
+ // 相机开始移动
943
934
  let cameraPosition = cameraManager.getCameraPosition()
944
935
  let visibleRegion = mapView.region
945
936
 
@@ -957,29 +948,9 @@ extension ExpoGaodeMapView {
957
948
  ]
958
949
  ]
959
950
 
960
- // 节流逻辑:0.1秒 内只触发一次
961
- if currentTime - lastCameraMoveTime >= cameraMoveThrottleInterval {
962
- // 超过节流时间,立即触发事件
963
- lastCameraMoveTime = currentTime
964
- onCameraMove(eventData)
965
- // 清除待处理的事件和定时器
966
- throttleTimer?.invalidate()
967
- throttleTimer = nil
968
- pendingCameraMoveData = nil
969
- } else {
970
- // 在节流时间内,缓存事件数据,使用定时器延迟触发
971
- pendingCameraMoveData = eventData
972
- throttleTimer?.invalidate()
973
-
974
- let delay = cameraMoveThrottleInterval - (currentTime - lastCameraMoveTime)
975
- throttleTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self] _ in
976
- guard let self = self, let data = self.pendingCameraMoveData else { return }
977
- self.lastCameraMoveTime = Date().timeIntervalSince1970
978
- self.onCameraMove(data)
979
- self.pendingCameraMoveData = nil
980
- self.throttleTimer = nil
981
- }
982
- }
951
+ // 直接触发事件,移除手动节流
952
+ // 建议在 JS 端进行 debounce/throttle 处理
953
+ onCameraMove(eventData)
983
954
  }
984
955
 
985
956
  /**
@@ -1,6 +1,14 @@
1
1
  import ExpoModulesCore
2
2
  import MAMapKit
3
3
 
4
+ enum MapType: Int, Enumerable {
5
+ case standard = 0
6
+ case satellite = 1
7
+ case night = 2
8
+ case navi = 3
9
+ case bus = 4
10
+ }
11
+
4
12
  /**
5
13
  * 高德地图视图 Module
6
14
  */
@@ -11,8 +19,8 @@ public class ExpoGaodeMapViewModule: Module {
11
19
  View(ExpoGaodeMapView.self) {
12
20
  Events("onMapPress", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
13
21
 
14
- Prop("mapType") { (view: ExpoGaodeMapView, type: Int) in
15
- view.mapType = type
22
+ Prop("mapType") { (view: ExpoGaodeMapView, type: MapType) in
23
+ view.mapType = type.rawValue
16
24
  }
17
25
 
18
26
  Prop("initialCameraPosition") { (view: ExpoGaodeMapView, position: [String: Any]?) in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-gaode-map",
3
- "version": "2.2.26-next.0",
3
+ "version": "2.2.26",
4
4
  "description": "A full-featured AMap (Gaode Map) React Native component library built with Expo Modules, providing map display, location services, overlays, and more.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -14,18 +14,18 @@
14
14
  "plugin/build"
15
15
  ],
16
16
  "scripts": {
17
- "build": "expo-module build && bun run build:plugin",
17
+ "build": "expo-module build && yarn build:plugin",
18
18
  "build:plugin": "tsc --project plugin/tsconfig.json",
19
19
  "clean": "expo-module clean && rm -rf plugin/build",
20
20
  "lint": "expo-module lint",
21
21
  "test": "expo-module test",
22
- "prepare": "expo-module prepare && bun run build:plugin",
22
+ "prepare": "expo-module prepare && yarn build:plugin",
23
23
  "prepublishOnly": "echo 'Skipping proofread check' && exit 0",
24
24
  "expo-module": "expo-module",
25
25
  "open:ios": "xed example/ios",
26
26
  "open:android": "open -a \"Android Studio\" example/android",
27
- "publish:next": "bun publish --tag next",
28
- "publish:latest": "bun publish --tag latest"
27
+ "publish:next": "npm publish --tag next",
28
+ "publish:latest": "npm publish --tag latest"
29
29
  },
30
30
  "keywords": [
31
31
  "react-native",
@@ -50,19 +50,15 @@
50
50
  "registry": "https://registry.npmjs.org/"
51
51
  },
52
52
  "devDependencies": {
53
- "@expo/config-plugins": "~54.0.4",
54
- "@testing-library/react-native": "^13.3.3",
55
53
  "@babel/runtime": "^7.25.7",
54
+ "@testing-library/react-native": "^13.3.3",
56
55
  "@types/jest": "^29.5.14",
57
56
  "@types/react": "~19.1.0",
58
- "eslint": "^9.15.0",
59
- "babel-preset-expo": "^54.0.8",
60
- "expo": "^54.0.31",
57
+ "expo": "^54.0.27",
61
58
  "expo-module-scripts": "^5.0.8",
62
59
  "jest": "~29.7.0",
63
60
  "jest-expo": "^54.0.16",
64
61
  "react-native": "0.81.5",
65
- "react-test-renderer": "19.1.0",
66
62
  "typescript": "^5.9.3"
67
63
  },
68
64
  "peerDependencies": {
@@ -1,4 +1,4 @@
1
- import { ConfigPlugin } from '@expo/config-plugins';
1
+ import { ConfigPlugin } from 'expo/config-plugins';
2
2
  /**
3
3
  * 高德地图插件配置类型
4
4
  */
@@ -13,6 +13,11 @@ export type GaodeMapPluginProps = {
13
13
  locationDescription?: string;
14
14
  /** 是否启用后台定位(Android & iOS) */
15
15
  enableBackgroundLocation?: boolean;
16
+ /**
17
+ * 自定义高德地图 SDK 路径(例如 Google Play 版本)
18
+ * 相对于项目根目录的路径,例如 "./libs/AMap_3DMap_V9.0.0_GooglePlay.aar"
19
+ */
20
+ customMapSdkPath?: string;
16
21
  };
17
22
  /**
18
23
  * 导出为可运行一次的插件
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const config_plugins_1 = require("@expo/config-plugins");
3
+ const config_plugins_1 = require("expo/config-plugins");
4
4
  const pkg = require('../../package.json');
5
5
  /** 默认定位权限描述 */
6
6
  const DEFAULT_LOCATION_USAGE = '需要访问您的位置信息以提供地图服务';
@@ -11,22 +11,24 @@ const withGaodeMapIOSPermissions = (config, props) => {
11
11
  if (props.enableLocation === false) {
12
12
  return config;
13
13
  }
14
- const description = props.locationDescription || DEFAULT_LOCATION_USAGE;
14
+ // 使用 IOSConfig.Permissions 简化权限配置
15
+ // 注意:locationDescription 的检查已移至 withGaodeMapInfoPlist 中以避免重复打印
16
+ const description = props.locationDescription;
17
+ const finalDescription = description || DEFAULT_LOCATION_USAGE;
15
18
  // 构建权限配置对象
16
19
  const permissionDefaults = {
17
20
  NSLocationWhenInUseUsageDescription: DEFAULT_LOCATION_USAGE,
18
21
  };
19
22
  const permissionValues = {
20
- NSLocationWhenInUseUsageDescription: description,
23
+ NSLocationWhenInUseUsageDescription: finalDescription,
21
24
  };
22
25
  // 如果启用后台定位,添加额外权限
23
26
  if (props.enableBackgroundLocation) {
24
27
  permissionDefaults.NSLocationAlwaysUsageDescription = DEFAULT_LOCATION_USAGE;
25
28
  permissionDefaults.NSLocationAlwaysAndWhenInUseUsageDescription = DEFAULT_LOCATION_USAGE;
26
- permissionValues.NSLocationAlwaysUsageDescription = description;
27
- permissionValues.NSLocationAlwaysAndWhenInUseUsageDescription = description;
29
+ permissionValues.NSLocationAlwaysUsageDescription = finalDescription;
30
+ permissionValues.NSLocationAlwaysAndWhenInUseUsageDescription = finalDescription;
28
31
  }
29
- // 使用 IOSConfig.Permissions 简化权限配置
30
32
  return config_plugins_1.IOSConfig.Permissions.createPermissionsPlugin(permissionDefaults)(config, permissionValues);
31
33
  };
32
34
  /**
@@ -34,6 +36,16 @@ const withGaodeMapIOSPermissions = (config, props) => {
34
36
  */
35
37
  const withGaodeMapInfoPlist = (config, props) => {
36
38
  return (0, config_plugins_1.withInfoPlist)(config, (config) => {
39
+ // 检查 iOS API Key
40
+ if (!props.iosKey && !props.androidKey) {
41
+ // 仅当两个平台都未配置时提示 Notice,避免打扰单平台开发者
42
+ // 这里使用 console.log 而不是 WarningAggregator,因为这只是一个提示
43
+ console.log('\u001b[33m[expo-gaode-map] Notice: 未在 app.json 中配置 API Key。请确保在应用启动时通过代码配置 Key,否则地图将无法加载。建议在 app.json 中配置 API Key 以避免运行时错误。\u001b[0m');
44
+ }
45
+ // 检查 locationDescription
46
+ if (props.enableLocation !== false && !props.locationDescription) {
47
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-gaode-map', `未配置 iOS 定位权限描述 (locationDescription)。将使用默认描述: "${DEFAULT_LOCATION_USAGE}"。建议在 app.json 中配置以满足 App Store 审核要求。`);
48
+ }
37
49
  // 添加高德地图 API Key
38
50
  if (props.iosKey) {
39
51
  config.modResults.AMapApiKey = props.iosKey;
@@ -146,12 +158,48 @@ const withGaodeMapAndroidManifest = (config, props) => {
146
158
  });
147
159
  };
148
160
  /**
149
- * Android: 修改 app/build.gradle(预留扩展)
161
+ * Android: 修改 app/build.gradle
162
+ * 当使用自定义 SDK 路径时,自动注入 exclude 规则以解决 duplicate class 冲突
150
163
  */
151
164
  const withGaodeMapAppBuildGradle = (config, props) => {
152
165
  return (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
153
- // Android 3D 地图 SDK 10.0+ 已内置搜索功能
154
- // 不需要额外的 Gradle 配置
166
+ if (props.customMapSdkPath) {
167
+ // 只有在配置了 customMapSdkPath 时才注入
168
+ const buildGradle = config.modResults.contents;
169
+ // 检查是否已经注入过
170
+ if (!buildGradle.includes('configurations.all { exclude group: "com.amap.api", module: "search" }')) {
171
+ const injection = `
172
+ // [expo-gaode-map] Auto-generated to fix duplicate class issues with custom SDK
173
+ configurations.all {
174
+ exclude group: "com.amap.api", module: "search"
175
+ exclude group: "com.amap.api", module: "location"
176
+ }
177
+ `;
178
+ config.modResults.contents = buildGradle + injection;
179
+ }
180
+ }
181
+ return config;
182
+ });
183
+ };
184
+ /**
185
+ * Android: 修改 gradle.properties 注入自定义 SDK 路径
186
+ */
187
+ const withGaodeMapAndroidSdk = (config, props) => {
188
+ return (0, config_plugins_1.withGradleProperties)(config, (config) => {
189
+ if (props.customMapSdkPath) {
190
+ const key = 'EXPO_GAODE_MAP_CUSTOM_SDK_PATH';
191
+ const existing = config.modResults.find((item) => item.type === 'property' && item.key === key);
192
+ if (existing && existing.type === 'property') {
193
+ existing.value = props.customMapSdkPath;
194
+ }
195
+ else {
196
+ config.modResults.push({
197
+ type: 'property',
198
+ key: key,
199
+ value: props.customMapSdkPath,
200
+ });
201
+ }
202
+ }
155
203
  return config;
156
204
  });
157
205
  };
@@ -159,10 +207,6 @@ const withGaodeMapAppBuildGradle = (config, props) => {
159
207
  * 主插件函数 - 组合所有修改器
160
208
  */
161
209
  const withGaodeMap = (config, props = {}) => {
162
- // 验证配置
163
- if (!props.iosKey && !props.androidKey) {
164
- config_plugins_1.WarningAggregator.addWarningIOS('expo-gaode-map', '未配置 API Key。请在 app.json 的 plugins 中配置 iosKey 和 androidKey');
165
- }
166
210
  // 应用 iOS 配置
167
211
  config = withGaodeMapIOSPermissions(config, props); // 使用 IOSConfig 添加权限
168
212
  config = withGaodeMapInfoPlist(config, props); // 添加 API Key 和后台模式
@@ -170,6 +214,7 @@ const withGaodeMap = (config, props = {}) => {
170
214
  config = withGaodeMapAndroidPermissions(config, props); // 使用 AndroidConfig 添加权限
171
215
  config = withGaodeMapAndroidManifest(config, props); // 添加服务和 API Key
172
216
  config = withGaodeMapAppBuildGradle(config, props);
217
+ config = withGaodeMapAndroidSdk(config, props); // 注入自定义 SDK 路径
173
218
  return config;
174
219
  };
175
220
  /**