expo-gaode-map 2.0.0-alpha.5 → 2.0.0-alpha.7

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.
@@ -0,0 +1,146 @@
1
+ # 🚀 发布检查清单
2
+
3
+ 快速参考 - 发布新版本时需要执行的命令
4
+
5
+ ## 📋 发布前检查
6
+
7
+ ```bash
8
+ # 1. 确保在主分支且代码最新
9
+ git checkout main
10
+ git pull origin main
11
+
12
+ # 2. 确保没有未提交的更改
13
+ git status
14
+ ```
15
+
16
+ ## 🔨 构建和测试
17
+
18
+ ```bash
19
+ # 1. 清理旧的构建产物
20
+ npm run clean
21
+
22
+ # 2. 安装依赖(如果需要)
23
+ npm install
24
+
25
+ # 3. 构建项目(包含主模块和 Config Plugin)
26
+ npm run build
27
+
28
+ # 4. 运行测试
29
+ npm test
30
+
31
+ # 5. 运行 lint 检查
32
+ npm run lint
33
+ ```
34
+
35
+ ## 📝 更新版本信息
36
+
37
+ ```bash
38
+ # 选择一个版本更新命令:
39
+
40
+ # Patch 版本 (1.0.0 -> 1.0.1) - 修复 bug
41
+ npm version patch
42
+
43
+ # Minor 版本 (1.0.0 -> 1.1.0) - 新增功能
44
+ npm version minor
45
+
46
+ # Major 版本 (1.0.0 -> 2.0.0) - 破坏性更新
47
+ npm version major
48
+
49
+ # 或手动编辑 package.json 中的 version 字段
50
+ ```
51
+
52
+ ## 📦 发布到 npm
53
+
54
+ ```bash
55
+ # 发布稳定版本(推荐)
56
+ npm run publish:latest
57
+
58
+ # 或发布预览版本(alpha/beta)
59
+ npm run publish:next
60
+ ```
61
+
62
+ ## 🏷️ 创建 Git 标签
63
+
64
+ ```bash
65
+ # 自动创建(如果使用 npm version 命令会自动创建标签)
66
+ # 或手动创建:
67
+
68
+ git tag -a v2.0.0 -m "Release v2.0.0"
69
+ git push origin v2.0.0
70
+ git push origin main
71
+ ```
72
+
73
+ ## 📄 完整命令序列(复制粘贴)
74
+
75
+ ```bash
76
+ # === 稳定版发布 ===
77
+ npm run clean && \
78
+ npm run build && \
79
+ npm test && \
80
+ npm run lint && \
81
+ npm version patch && \
82
+ npm run publish:latest && \
83
+ git push origin main && \
84
+ git push --tags
85
+
86
+ # === 预览版发布 ===
87
+ npm run clean && \
88
+ npm run build && \
89
+ npm test && \
90
+ npm run lint && \
91
+ npm version prerelease --preid=alpha && \
92
+ npm run publish:next && \
93
+ git push origin main && \
94
+ git push --tags
95
+ ```
96
+
97
+ ## ✅ 发布后验证
98
+
99
+ ```bash
100
+ # 1. 检查 npm 上的版本
101
+ npm view expo-gaode-map version
102
+
103
+ # 2. 在测试项目中安装新版本
104
+ cd /path/to/test-project
105
+ npm install expo-gaode-map@latest
106
+
107
+ # 3. 验证 Config Plugin 是否工作
108
+ npx expo prebuild
109
+ ```
110
+
111
+ ## 🔙 回滚(如有问题)
112
+
113
+ ```bash
114
+ # 废弃有问题的版本
115
+ npm deprecate expo-gaode-map@版本号 "此版本存在问题,请使用 x.x.x 版本"
116
+ ```
117
+
118
+ ## 📊 检查清单
119
+
120
+ - [ ] 代码已提交且推送到 main 分支
121
+ - [ ] 运行 `npm run clean`
122
+ - [ ] 运行 `npm run build` 成功
123
+ - [ ] 运行 `npm test` 通过
124
+ - [ ] 运行 `npm run lint` 无错误
125
+ - [ ] 确认 `build/` 目录存在
126
+ - [ ] 确认 `plugin/build/` 目录存在
127
+ - [ ] 更新了 CHANGELOG.md
128
+ - [ ] 版本号已更新
129
+ - [ ] 发布到 npm 成功
130
+ - [ ] Git 标签已创建并推送
131
+ - [ ] 在 GitHub 创建了 Release
132
+ - [ ] 测试项目中验证新版本可用
133
+
134
+ ## 💡 常用版本号规则
135
+
136
+ | 类型 | 命令 | 示例 | 说明 |
137
+ |------|------|------|------|
138
+ | Patch | `npm version patch` | 1.0.0 → 1.0.1 | Bug 修复 |
139
+ | Minor | `npm version minor` | 1.0.0 → 1.1.0 | 新功能(向下兼容) |
140
+ | Major | `npm version major` | 1.0.0 → 2.0.0 | 破坏性更新 |
141
+ | Prerelease | `npm version prerelease --preid=alpha` | 1.0.0 → 1.0.1-alpha.0 | 预发布版本 |
142
+
143
+ ## 🔗 相关文档
144
+
145
+ - 详细发布流程: [PUBLISHING.md](./PUBLISHING.md)
146
+ - 项目部署文档: [DEPLOY_DOCS.md](./DEPLOY_DOCS.md)
@@ -6,8 +6,6 @@ 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.Paint
10
- import android.graphics.Path
11
9
  import android.os.Handler
12
10
  import android.os.Looper
13
11
  import android.view.View
@@ -54,7 +52,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
54
52
  override fun generateLayoutParams(lp: android.view.ViewGroup.LayoutParams?): LayoutParams {
55
53
  return when (lp) {
56
54
  is LayoutParams -> lp
57
- is MarginLayoutParams -> android.widget.LinearLayout.LayoutParams(lp)
55
+ is android.widget.FrameLayout.LayoutParams -> LayoutParams(lp.width, lp.height)
56
+ is MarginLayoutParams -> LayoutParams(lp.width, lp.height)
58
57
  else -> LayoutParams(
59
58
  lp?.width ?: LayoutParams.WRAP_CONTENT,
60
59
  lp?.height ?: LayoutParams.WRAP_CONTENT
@@ -505,9 +504,9 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
505
504
 
506
505
  fun handleMarkerClick(marker: Marker): Boolean {
507
506
  markerViewMap[marker]?.let { view ->
508
- view.onMarkerPress?.invoke(mapOf(
509
- "latitude" to marker.position.latitude,
510
- "longitude" to marker.position.longitude
507
+ view.onMarkerPress.invoke(mapOf(
508
+ "latitude" to marker.position.latitude,
509
+ "longitude" to marker.position.longitude
511
510
  ))
512
511
  // 只有在没有自定义内容(children)且有 title 或 snippet 时才显示信息窗口
513
512
  // 如果有自定义内容,说明用户已经自定义了显示内容,不需要默认信息窗口
@@ -576,39 +575,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
576
575
  }
577
576
  }
578
577
 
579
- /**
580
- * 创建默认 marker 图标 (红色大头针)
581
- */
582
- private fun createDefaultMarkerBitmap(): Bitmap {
583
- val width = 48
584
- val height = 72
585
- val bitmap = createBitmap(width, height)
586
- val canvas = Canvas(bitmap)
587
-
588
- val paint = Paint(Paint.ANTI_ALIAS_FLAG)
589
- paint.color = "#FF5252".toColorInt()
590
- paint.style = Paint.Style.FILL
591
-
592
- // 绘制圆形顶部
593
- canvas.drawCircle(width / 2f, width / 2f, width / 2f - 2, paint)
594
-
595
- // 绘制尖端
596
- val path = Path()
597
- path.moveTo(width / 2f, height.toFloat())
598
- path.lineTo(width / 4f, width / 2f + 10f)
599
- path.lineTo(3 * width / 4f, width / 2f + 10f)
600
- path.close()
601
- canvas.drawPath(path, paint)
602
-
603
- // 绘制白色边框
604
- paint.color = Color.WHITE
605
- paint.style = Paint.Style.STROKE
606
- paint.strokeWidth = 3f
607
- canvas.drawCircle(width / 2f, width / 2f, width / 2f - 4, paint)
608
-
609
- return bitmap
610
- }
611
-
578
+
612
579
  /**
613
580
  * 将视图转换为 Bitmap
614
581
  */
@@ -642,29 +609,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
642
609
  }
643
610
  }
644
611
 
645
- /**
646
- * 创建组合 Bitmap:默认 marker + 自定义内容
647
- */
648
- private fun createCombinedBitmap(): Bitmap? {
649
- val customBitmap = createBitmapFromView() ?: return null
650
- val markerBitmap = createDefaultMarkerBitmap()
651
-
652
- val totalWidth = maxOf(markerBitmap.width, customBitmap.width)
653
- val totalHeight = markerBitmap.height + customBitmap.height + 10
654
-
655
- val combinedBitmap = Bitmap.createBitmap(totalWidth, totalHeight, Bitmap.Config.ARGB_8888)
656
- val canvas = Canvas(combinedBitmap)
657
-
658
- val customX = (totalWidth - customBitmap.width) / 2f
659
- canvas.drawBitmap(customBitmap, customX, 0f, null)
660
-
661
- val markerX = (totalWidth - markerBitmap.width) / 2f
662
- val markerY = customBitmap.height + 10f
663
- canvas.drawBitmap(markerBitmap, markerX, markerY, null)
664
-
665
- return combinedBitmap
666
- }
667
-
612
+
668
613
  /**
669
614
  * 更新 marker 图标
670
615
  */
package/app.plugin.js ADDED
@@ -0,0 +1,11 @@
1
+
2
+ // Expo Config Plugin 入口文件
3
+ // 这个文件会在用户运行 npx expo prebuild 时被调用
4
+
5
+ const { createRunOncePlugin } = require('@expo/config-plugins');
6
+
7
+ // 导入编译后的插件
8
+ const withGaodeMap = require('./plugin/build/withGaodeMap').default;
9
+
10
+ // 导出插件
11
+ module.exports = withGaodeMap;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"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"}
@@ -3,11 +3,21 @@
3
3
  * 提供 SDK 初始化、定位、权限管理等功能
4
4
  */
5
5
  import { requireNativeModule } from 'expo';
6
- // 获取原生模块实例
7
- const nativeModule = requireNativeModule('ExpoGaodeMap');
6
+ // 获取原生模块实例 - 添加容错处理
7
+ let nativeModule = null;
8
+ try {
9
+ nativeModule = requireNativeModule('ExpoGaodeMap');
10
+ }
11
+ catch (error) {
12
+ console.warn('ExpoGaodeMap 原生模块加载失败:', error);
13
+ }
14
+ // 如果模块加载失败,创建一个空的代理对象防止崩溃
15
+ if (!nativeModule) {
16
+ console.error('ExpoGaodeMap: 原生模块不可用,请检查配置');
17
+ }
8
18
  // 扩展原生模块,添加便捷方法
9
19
  const ExpoGaodeMapModuleWithHelpers = {
10
- ...nativeModule,
20
+ ...(nativeModule || {}),
11
21
  /**
12
22
  * 添加定位监听器(便捷方法)
13
23
  * 自动订阅 onLocationUpdate 事件,提供容错处理
@@ -17,7 +27,7 @@ const ExpoGaodeMapModuleWithHelpers = {
17
27
  */
18
28
  addLocationListener(listener) {
19
29
  // 使用可选链和空值合并,确保即使模块不可用也不会崩溃
20
- return nativeModule.addListener?.('onLocationUpdate', listener) || {
30
+ return nativeModule?.addListener?.('onLocationUpdate', listener) || {
21
31
  remove: () => { },
22
32
  };
23
33
  },
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoGaodeMapModule.js","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAgPzD,WAAW;AACX,MAAM,YAAY,GAAG,mBAAmB,CAAqB,cAAc,CAAC,CAAC;AAE7E,gBAAgB;AAChB,MAAM,6BAA6B,GAAG;IACpC,GAAG,YAAY;IAEf;;;;;;OAMG;IACH,mBAAmB,CAAC,QAA0B;QAC5C,4BAA4B;QAC5B,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,kBAAkB,EAAE,QAAQ,CAAC,IAAI;YACjE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;SACjB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,6BAAmD,CAAC","sourcesContent":["/**\n * 高德地图原生模块\n * 提供 SDK 初始化、定位、权限管理等功能\n */\n\nimport { NativeModule, requireNativeModule } from 'expo';\nimport type { ExpoGaodeMapModuleEvents } from './ExpoGaodeMap.types';\nimport type {\n LatLng,\n CoordinateType,\n Coordinates,\n ReGeocode,\n LocationMode,\n LocationAccuracy,\n LocationListener,\n} from './types';\n\n/**\n * SDK 配置参数\n */\nexport interface SDKConfig {\n /** Android 平台的高德地图 API Key */\n androidKey?: string;\n /** iOS 平台的高德地图 API Key */\n iosKey?: string;\n}\n\n/**\n * 权限状态\n */\nexport interface PermissionStatus {\n /** 是否已授权 */\n granted: boolean;\n /** iOS 权限状态字符串 */\n status?: 'notDetermined' | 'restricted' | 'denied' | 'authorizedAlways' | 'authorizedWhenInUse' | 'unknown';\n /** Android 精确位置权限 */\n fineLocation?: boolean;\n /** Android 粗略位置权限 */\n coarseLocation?: boolean;\n}\n\n/**\n * 高德地图原生模块类声明\n */\ndeclare class ExpoGaodeMapModule extends NativeModule<ExpoGaodeMapModuleEvents> {\n // ==================== SDK 初始化 ====================\n \n /**\n * 初始化高德地图 SDK\n * @param config SDK 配置参数,包含 Android 和 iOS 的 API Key\n */\n initSDK(config: SDKConfig): void;\n \n /**\n * 获取高德地图 SDK 版本号\n * @returns SDK 版本字符串\n */\n getVersion(): string;\n \n // ==================== 定位控制 ====================\n \n /**\n * 开始连续定位\n * 启动后会持续接收位置更新,通过 onLocationUpdate 事件回调\n */\n start(): void;\n \n /**\n * 停止定位\n * 停止接收位置更新\n */\n stop(): void;\n \n /**\n * 检查是否正在定位\n * @returns Promise<boolean> 是否正在定位\n */\n isStarted(): Promise<boolean>;\n \n /**\n * 获取当前位置(单次定位)\n * @returns Promise<Coordinates | ReGeocode> 位置信息,包含坐标和可选的逆地理编码信息\n */\n getCurrentLocation(): Promise<Coordinates | ReGeocode>;\n \n /**\n * 坐标转换\n * 将其他坐标系的坐标转换为高德地图使用的 GCJ-02 坐标系\n * @param coordinate 需要转换的坐标\n * @param type 原坐标系类型\n * @returns Promise<LatLng> 转换后的 GCJ-02 坐标\n */\n coordinateConvert(coordinate: LatLng, type: CoordinateType): Promise<LatLng>;\n \n // ==================== 定位配置 ====================\n \n /**\n * 设置是否返回逆地理编码信息\n * @param isReGeocode true: 返回地址信息; false: 只返回坐标\n */\n setLocatingWithReGeocode(isReGeocode: boolean): void;\n \n /**\n * 设置定位模式(Android)\n * @param mode 定位模式:0-低功耗, 1-仅设备, 2-高精度\n */\n setLocationMode(mode: LocationMode): void;\n \n /**\n * 设置定位间隔(毫秒)\n * @param interval 定位间隔时间,单位毫秒,默认 2000ms\n */\n setInterval(interval: number): void;\n \n /**\n * 设置是否单次定位(Android)\n * @param isOnceLocation true: 单次定位; false: 连续定位\n */\n setOnceLocation(isOnceLocation: boolean): void;\n \n /**\n * 设置是否使用设备传感器(Android)\n * @param sensorEnable true: 使用传感器; false: 不使用\n */\n setSensorEnable(sensorEnable: boolean): void;\n \n /**\n * 设置是否允许 WiFi 扫描(Android)\n * @param wifiScan true: 允许; false: 不允许\n */\n setWifiScan(wifiScan: boolean): void;\n \n /**\n * 设置是否 GPS 优先(Android)\n * @param gpsFirst true: GPS 优先; false: 网络优先\n */\n setGpsFirst(gpsFirst: boolean): void;\n \n /**\n * 设置是否等待 WiFi 列表刷新(Android)\n * @param onceLocationLatest true: 等待; false: 不等待\n */\n setOnceLocationLatest(onceLocationLatest: boolean): void;\n \n /**\n * 设置逆地理编码语言\n * @param language 语言代码,如 \"zh-CN\", \"en\"\n */\n setGeoLanguage(language: string): void;\n \n /**\n * 设置是否使用缓存策略(Android)\n * @param locationCacheEnable true: 使用缓存; false: 不使用\n */\n setLocationCacheEnable(locationCacheEnable: boolean): void;\n \n /**\n * 设置网络请求超时时间(Android)\n * @param httpTimeOut 超时时间,单位毫秒\n */\n setHttpTimeOut(httpTimeOut: number): void;\n \n /**\n * 设置期望的定位精度(iOS)\n * @param accuracy 精度级别:0-最佳, 1-10米, 2-100米, 3-1公里, 4-3公里\n */\n setDesiredAccuracy(accuracy: LocationAccuracy): void;\n \n /**\n * 设置定位超时时间(秒)\n * @param timeout 超时时间,单位秒,默认 10 秒\n */\n setLocationTimeout(timeout: number): void;\n \n /**\n * 设置逆地理编码超时时间(秒)\n * @param timeout 超时时间,单位秒,默认 5 秒\n */\n setReGeocodeTimeout(timeout: number): void;\n \n /**\n * 设置距离过滤器(米)(iOS)\n * 只有移动超过指定距离才会更新位置\n * @param distance 距离阈值,单位米\n */\n setDistanceFilter(distance: number): void;\n \n /**\n * 设置是否自动暂停位置更新(iOS)\n * @param pauses true: 自动暂停; false: 不暂停\n */\n setPausesLocationUpdatesAutomatically(pauses: boolean): void;\n \n /**\n * 设置是否允许后台定位(iOS)\n * @param allows true: 允许; false: 不允许\n */\n setAllowsBackgroundLocationUpdates(allows: boolean): void;\n \n /**\n * 设置定位协议\n * @param protocol 协议类型\n */\n setLocationProtocol(protocol: string): void;\n \n // ==================== 方向更新 (iOS) ====================\n \n /**\n * 开始更新设备方向(罗盘朝向)\n * 通过 onHeadingUpdate 事件接收方向更新\n * @platform ios\n */\n startUpdatingHeading(): void;\n \n /**\n * 停止更新设备方向\n * @platform ios\n */\n stopUpdatingHeading(): void;\n \n // ==================== 权限管理 ====================\n \n /**\n * 检查位置权限状态\n * @returns Promise<PermissionStatus> 权限状态\n */\n checkLocationPermission(): Promise<PermissionStatus>;\n \n /**\n * 请求位置权限\n * @returns Promise<PermissionStatus> 请求后的权限状态\n */\n requestLocationPermission(): Promise<PermissionStatus>;\n \n // ==================== 便捷方法 ====================\n \n /**\n * 添加定位监听器(便捷方法)\n * 封装了 addListener,提供更简洁的 API\n * @param listener 定位回调函数\n * @returns 订阅对象,调用 remove() 取消监听\n */\n addLocationListener(listener: LocationListener): { remove: () => void };\n}\n\n// 获取原生模块实例\nconst nativeModule = requireNativeModule<ExpoGaodeMapModule>('ExpoGaodeMap');\n\n// 扩展原生模块,添加便捷方法\nconst ExpoGaodeMapModuleWithHelpers = {\n ...nativeModule,\n \n /**\n * 添加定位监听器(便捷方法)\n * 自动订阅 onLocationUpdate 事件,提供容错处理\n * @param listener 定位回调函数\n * @returns 订阅对象,调用 remove() 取消监听\n * @throws 如果底层模块不可用,返回一个空操作的订阅对象\n */\n addLocationListener(listener: LocationListener): { remove: () => void } {\n // 使用可选链和空值合并,确保即使模块不可用也不会崩溃\n return nativeModule.addListener?.('onLocationUpdate', listener) || {\n remove: () => {},\n };\n },\n};\n\nexport default ExpoGaodeMapModuleWithHelpers as ExpoGaodeMapModule;\n"]}
1
+ {"version":3,"file":"ExpoGaodeMapModule.js","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAgPzD,oBAAoB;AACpB,IAAI,YAAY,GAA8B,IAAI,CAAC;AAEnD,IAAI,CAAC;IACH,YAAY,GAAG,mBAAmB,CAAqB,cAAc,CAAC,CAAC;AACzE,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,0BAA0B;AAC1B,IAAI,CAAC,YAAY,EAAE,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAC/C,CAAC;AAED,gBAAgB;AAChB,MAAM,6BAA6B,GAAG;IACpC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IAEvB;;;;;;OAMG;IACH,mBAAmB,CAAC,QAA0B;QAC5C,4BAA4B;QAC5B,OAAO,YAAY,EAAE,WAAW,EAAE,CAAC,kBAAkB,EAAE,QAAQ,CAAC,IAAI;YAClE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;SACjB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,6BAAmD,CAAC","sourcesContent":["/**\n * 高德地图原生模块\n * 提供 SDK 初始化、定位、权限管理等功能\n */\n\nimport { NativeModule, requireNativeModule } from 'expo';\nimport type { ExpoGaodeMapModuleEvents } from './ExpoGaodeMap.types';\nimport type {\n LatLng,\n CoordinateType,\n Coordinates,\n ReGeocode,\n LocationMode,\n LocationAccuracy,\n LocationListener,\n} from './types';\n\n/**\n * SDK 配置参数\n */\nexport interface SDKConfig {\n /** Android 平台的高德地图 API Key */\n androidKey?: string;\n /** iOS 平台的高德地图 API Key */\n iosKey?: string;\n}\n\n/**\n * 权限状态\n */\nexport interface PermissionStatus {\n /** 是否已授权 */\n granted: boolean;\n /** iOS 权限状态字符串 */\n status?: 'notDetermined' | 'restricted' | 'denied' | 'authorizedAlways' | 'authorizedWhenInUse' | 'unknown';\n /** Android 精确位置权限 */\n fineLocation?: boolean;\n /** Android 粗略位置权限 */\n coarseLocation?: boolean;\n}\n\n/**\n * 高德地图原生模块类声明\n */\ndeclare class ExpoGaodeMapModule extends NativeModule<ExpoGaodeMapModuleEvents> {\n // ==================== SDK 初始化 ====================\n \n /**\n * 初始化高德地图 SDK\n * @param config SDK 配置参数,包含 Android 和 iOS 的 API Key\n */\n initSDK(config: SDKConfig): void;\n \n /**\n * 获取高德地图 SDK 版本号\n * @returns SDK 版本字符串\n */\n getVersion(): string;\n \n // ==================== 定位控制 ====================\n \n /**\n * 开始连续定位\n * 启动后会持续接收位置更新,通过 onLocationUpdate 事件回调\n */\n start(): void;\n \n /**\n * 停止定位\n * 停止接收位置更新\n */\n stop(): void;\n \n /**\n * 检查是否正在定位\n * @returns Promise<boolean> 是否正在定位\n */\n isStarted(): Promise<boolean>;\n \n /**\n * 获取当前位置(单次定位)\n * @returns Promise<Coordinates | ReGeocode> 位置信息,包含坐标和可选的逆地理编码信息\n */\n getCurrentLocation(): Promise<Coordinates | ReGeocode>;\n \n /**\n * 坐标转换\n * 将其他坐标系的坐标转换为高德地图使用的 GCJ-02 坐标系\n * @param coordinate 需要转换的坐标\n * @param type 原坐标系类型\n * @returns Promise<LatLng> 转换后的 GCJ-02 坐标\n */\n coordinateConvert(coordinate: LatLng, type: CoordinateType): Promise<LatLng>;\n \n // ==================== 定位配置 ====================\n \n /**\n * 设置是否返回逆地理编码信息\n * @param isReGeocode true: 返回地址信息; false: 只返回坐标\n */\n setLocatingWithReGeocode(isReGeocode: boolean): void;\n \n /**\n * 设置定位模式(Android)\n * @param mode 定位模式:0-低功耗, 1-仅设备, 2-高精度\n */\n setLocationMode(mode: LocationMode): void;\n \n /**\n * 设置定位间隔(毫秒)\n * @param interval 定位间隔时间,单位毫秒,默认 2000ms\n */\n setInterval(interval: number): void;\n \n /**\n * 设置是否单次定位(Android)\n * @param isOnceLocation true: 单次定位; false: 连续定位\n */\n setOnceLocation(isOnceLocation: boolean): void;\n \n /**\n * 设置是否使用设备传感器(Android)\n * @param sensorEnable true: 使用传感器; false: 不使用\n */\n setSensorEnable(sensorEnable: boolean): void;\n \n /**\n * 设置是否允许 WiFi 扫描(Android)\n * @param wifiScan true: 允许; false: 不允许\n */\n setWifiScan(wifiScan: boolean): void;\n \n /**\n * 设置是否 GPS 优先(Android)\n * @param gpsFirst true: GPS 优先; false: 网络优先\n */\n setGpsFirst(gpsFirst: boolean): void;\n \n /**\n * 设置是否等待 WiFi 列表刷新(Android)\n * @param onceLocationLatest true: 等待; false: 不等待\n */\n setOnceLocationLatest(onceLocationLatest: boolean): void;\n \n /**\n * 设置逆地理编码语言\n * @param language 语言代码,如 \"zh-CN\", \"en\"\n */\n setGeoLanguage(language: string): void;\n \n /**\n * 设置是否使用缓存策略(Android)\n * @param locationCacheEnable true: 使用缓存; false: 不使用\n */\n setLocationCacheEnable(locationCacheEnable: boolean): void;\n \n /**\n * 设置网络请求超时时间(Android)\n * @param httpTimeOut 超时时间,单位毫秒\n */\n setHttpTimeOut(httpTimeOut: number): void;\n \n /**\n * 设置期望的定位精度(iOS)\n * @param accuracy 精度级别:0-最佳, 1-10米, 2-100米, 3-1公里, 4-3公里\n */\n setDesiredAccuracy(accuracy: LocationAccuracy): void;\n \n /**\n * 设置定位超时时间(秒)\n * @param timeout 超时时间,单位秒,默认 10 秒\n */\n setLocationTimeout(timeout: number): void;\n \n /**\n * 设置逆地理编码超时时间(秒)\n * @param timeout 超时时间,单位秒,默认 5 秒\n */\n setReGeocodeTimeout(timeout: number): void;\n \n /**\n * 设置距离过滤器(米)(iOS)\n * 只有移动超过指定距离才会更新位置\n * @param distance 距离阈值,单位米\n */\n setDistanceFilter(distance: number): void;\n \n /**\n * 设置是否自动暂停位置更新(iOS)\n * @param pauses true: 自动暂停; false: 不暂停\n */\n setPausesLocationUpdatesAutomatically(pauses: boolean): void;\n \n /**\n * 设置是否允许后台定位(iOS)\n * @param allows true: 允许; false: 不允许\n */\n setAllowsBackgroundLocationUpdates(allows: boolean): void;\n \n /**\n * 设置定位协议\n * @param protocol 协议类型\n */\n setLocationProtocol(protocol: string): void;\n \n // ==================== 方向更新 (iOS) ====================\n \n /**\n * 开始更新设备方向(罗盘朝向)\n * 通过 onHeadingUpdate 事件接收方向更新\n * @platform ios\n */\n startUpdatingHeading(): void;\n \n /**\n * 停止更新设备方向\n * @platform ios\n */\n stopUpdatingHeading(): void;\n \n // ==================== 权限管理 ====================\n \n /**\n * 检查位置权限状态\n * @returns Promise<PermissionStatus> 权限状态\n */\n checkLocationPermission(): Promise<PermissionStatus>;\n \n /**\n * 请求位置权限\n * @returns Promise<PermissionStatus> 请求后的权限状态\n */\n requestLocationPermission(): Promise<PermissionStatus>;\n \n // ==================== 便捷方法 ====================\n \n /**\n * 添加定位监听器(便捷方法)\n * 封装了 addListener,提供更简洁的 API\n * @param listener 定位回调函数\n * @returns 订阅对象,调用 remove() 取消监听\n */\n addLocationListener(listener: LocationListener): { remove: () => void };\n}\n\n// 获取原生模块实例 - 添加容错处理\nlet nativeModule: ExpoGaodeMapModule | null = null;\n\ntry {\n nativeModule = requireNativeModule<ExpoGaodeMapModule>('ExpoGaodeMap');\n} catch (error) {\n console.warn('ExpoGaodeMap 原生模块加载失败:', error);\n}\n\n// 如果模块加载失败,创建一个空的代理对象防止崩溃\nif (!nativeModule) {\n console.error('ExpoGaodeMap: 原生模块不可用,请检查配置');\n}\n\n// 扩展原生模块,添加便捷方法\nconst ExpoGaodeMapModuleWithHelpers = {\n ...(nativeModule || {}),\n \n /**\n * 添加定位监听器(便捷方法)\n * 自动订阅 onLocationUpdate 事件,提供容错处理\n * @param listener 定位回调函数\n * @returns 订阅对象,调用 remove() 取消监听\n * @throws 如果底层模块不可用,返回一个空操作的订阅对象\n */\n addLocationListener(listener: LocationListener): { remove: () => void } {\n // 使用可选链和空值合并,确保即使模块不可用也不会崩溃\n return nativeModule?.addListener?.('onLocationUpdate', listener) || {\n remove: () => {},\n };\n },\n};\n\nexport default ExpoGaodeMapModuleWithHelpers as ExpoGaodeMapModule;\n"]}
@@ -178,11 +178,12 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
178
178
  private func initLocationManager() {
179
179
  locationManager = AMapLocationManager()
180
180
  locationManager?.delegate = self
181
- // 推荐配置:百米精度,快速定位(2-3秒)
181
+ // 推荐配置:百米精度,快速定位
182
182
  locationManager?.desiredAccuracy = kCLLocationAccuracyHundredMeters
183
183
  locationManager?.distanceFilter = 10
184
- locationManager?.locationTimeout = 2 // 2秒超时
185
- locationManager?.reGeocodeTimeout = 2 // 2秒超时
184
+ // 增加超时时间,避免首次授权时超时(首次定位建议10秒以上)
185
+ locationManager?.locationTimeout = 10 // 10秒超时
186
+ locationManager?.reGeocodeTimeout = 5 // 5秒超时
186
187
  locationManager?.locatingWithReGeocode = true
187
188
 
188
189
  // iOS 9 之前:防止后台被系统挂起(默认关闭,用户可通过 setPausesLocationUpdatesAutomatically 配置)
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "expo-gaode-map",
3
- "version": "2.0.0-alpha.5",
3
+ "version": "2.0.0-alpha.7",
4
4
  "description": "一个功能完整的高德地图 React Native 组件库,基于 Expo Modules 开发,提供地图显示、定位、覆盖物等功能。",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
7
7
  "scripts": {
8
- "build": "expo-module build",
9
- "clean": "expo-module clean",
8
+ "build": "expo-module build && npm run build:plugin",
9
+ "build:plugin": "tsc --project plugin/tsconfig.json",
10
+ "clean": "expo-module clean && rm -rf plugin/build",
10
11
  "lint": "expo-module lint",
11
12
  "test": "expo-module test",
12
- "prepare": "expo-module prepare",
13
+ "prepare": "expo-module prepare && npm run build:plugin",
13
14
  "prepublishOnly": "expo-module prepublishOnly",
14
15
  "expo-module": "expo-module",
15
16
  "open:ios": "xed example/ios",
@@ -35,10 +36,12 @@
35
36
  "supercluster": "^8.0.1"
36
37
  },
37
38
  "devDependencies": {
39
+ "@expo/config-plugins": "^9.1.7",
38
40
  "@types/react": "~19.1.0",
39
41
  "expo": "^54.0.18",
40
42
  "expo-module-scripts": "^5.0.7",
41
- "react-native": "0.81.5"
43
+ "react-native": "0.81.5",
44
+ "typescript": "^5.9.3"
42
45
  },
43
46
  "peerDependencies": {
44
47
  "expo": "*",
@@ -0,0 +1,52 @@
1
+ # Expo Config Plugin for expo-gaode-map
2
+
3
+ 这是 `expo-gaode-map` 的 Expo Config Plugin 实现。
4
+
5
+ ## 功能
6
+
7
+ - 自动配置 iOS 和 Android 平台的高德地图 API Key
8
+ - 自动添加定位权限
9
+ - 自动初始化高德地图 SDK
10
+
11
+ ## 开发
12
+
13
+ ### 构建插件
14
+
15
+ ```bash
16
+ npm run build:plugin
17
+ ```
18
+
19
+ ### 目录结构
20
+
21
+ ```
22
+ plugin/
23
+ ├── src/
24
+ │ └── withGaodeMap.ts # 插件源代码
25
+ ├── build/ # 编译输出目录
26
+ ├── tsconfig.json # TypeScript 配置
27
+ └── README.md # 本文件
28
+ ```
29
+
30
+ ## 使用方法
31
+
32
+ 请查看主文档: [CONFIG_PLUGIN.md](../docs/CONFIG_PLUGIN.md)
33
+
34
+ ## 技术细节
35
+
36
+ ### 修改的文件
37
+
38
+ **iOS:**
39
+ - `Info.plist` - 添加 API Key 和权限
40
+ - `AppDelegate.m` - 添加 SDK 初始化代码
41
+
42
+ **Android:**
43
+ - `AndroidManifest.xml` - 添加 API Key 和权限
44
+
45
+ ### 依赖
46
+
47
+ - `@expo/config-plugins` - Expo 配置插件核心库
48
+
49
+ ## 参考
50
+
51
+ - [Expo Config Plugins](https://docs.expo.dev/config-plugins/introduction/)
52
+ - [Creating a Config Plugin](https://docs.expo.dev/config-plugins/plugins-and-mods/)
@@ -0,0 +1,20 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ /**
3
+ * 高德地图插件配置类型
4
+ */
5
+ export type GaodeMapPluginProps = {
6
+ /** iOS 平台 API Key */
7
+ iosApiKey?: string;
8
+ /** Android 平台 API Key */
9
+ androidApiKey?: string;
10
+ /** 是否启用定位功能 */
11
+ enableLocation?: boolean;
12
+ /** iOS 定位权限描述 */
13
+ locationDescription?: string;
14
+ };
15
+ /**
16
+ * 导出为可运行一次的插件
17
+ * 这确保插件只会运行一次,即使在配置中被多次引用
18
+ */
19
+ declare const _default: ConfigPlugin<GaodeMapPluginProps>;
20
+ export default _default;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_plugins_1 = require("@expo/config-plugins");
4
+ /**
5
+ * iOS: 修改 Info.plist 添加 API Key 和权限
6
+ */
7
+ const withGaodeMapInfoPlist = (config, props) => {
8
+ return (0, config_plugins_1.withInfoPlist)(config, (config) => {
9
+ // 添加高德地图 API Key
10
+ if (props.iosApiKey) {
11
+ config.modResults.AMapApiKey = props.iosApiKey;
12
+ }
13
+ // 添加定位相关权限
14
+ if (props.enableLocation !== false) {
15
+ const description = props.locationDescription || '需要访问您的位置信息以提供地图服务';
16
+ config.modResults.NSLocationWhenInUseUsageDescription = description;
17
+ config.modResults.NSLocationAlwaysUsageDescription = description;
18
+ config.modResults.NSLocationAlwaysAndWhenInUseUsageDescription = description;
19
+ }
20
+ // 添加后台定位模式(如果需要)
21
+ if (props.enableLocation !== false) {
22
+ if (!config.modResults.UIBackgroundModes) {
23
+ config.modResults.UIBackgroundModes = [];
24
+ }
25
+ if (!config.modResults.UIBackgroundModes.includes('location')) {
26
+ config.modResults.UIBackgroundModes.push('location');
27
+ }
28
+ }
29
+ return config;
30
+ });
31
+ };
32
+ /**
33
+ * iOS: 修改 AppDelegate 添加初始化代码
34
+ */
35
+ const withGaodeMapAppDelegate = (config, props) => {
36
+ return (0, config_plugins_1.withAppDelegate)(config, (config) => {
37
+ if (!props.iosApiKey) {
38
+ return config;
39
+ }
40
+ let contents = config.modResults.contents;
41
+ // 添加 import 语句
42
+ if (!contents.includes('#import <AMapFoundationKit/AMapFoundationKit.h>')) {
43
+ // 在 #import "AppDelegate.h" 之后添加
44
+ contents = contents.replace(/#import "AppDelegate.h"/g, `#import "AppDelegate.h"\n#import <AMapFoundationKit/AMapFoundationKit.h>`);
45
+ }
46
+ // 在 didFinishLaunchingWithOptions 方法中添加初始化代码
47
+ const initCode = ` [AMapServices sharedServices].apiKey = @"${props.iosApiKey}";`;
48
+ if (!contents.includes(initCode)) {
49
+ // 在 didFinishLaunchingWithOptions 方法的开始处添加
50
+ contents = contents.replace(/(- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions\s*\{)/g, `$1\n${initCode}`);
51
+ }
52
+ config.modResults.contents = contents;
53
+ return config;
54
+ });
55
+ };
56
+ /**
57
+ * Android: 修改 AndroidManifest.xml 添加 API Key 和权限
58
+ */
59
+ const withGaodeMapAndroidManifest = (config, props) => {
60
+ return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
61
+ const androidManifest = config.modResults.manifest;
62
+ // 添加权限
63
+ if (props.enableLocation !== false) {
64
+ const permissions = [
65
+ 'android.permission.ACCESS_COARSE_LOCATION',
66
+ 'android.permission.ACCESS_FINE_LOCATION',
67
+ 'android.permission.ACCESS_WIFI_STATE',
68
+ 'android.permission.ACCESS_NETWORK_STATE',
69
+ 'android.permission.CHANGE_WIFI_STATE',
70
+ 'android.permission.INTERNET',
71
+ 'android.permission.WRITE_EXTERNAL_STORAGE',
72
+ 'android.permission.READ_EXTERNAL_STORAGE',
73
+ 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
74
+ ];
75
+ if (!androidManifest['uses-permission']) {
76
+ androidManifest['uses-permission'] = [];
77
+ }
78
+ permissions.forEach((permission) => {
79
+ const hasPermission = androidManifest['uses-permission']?.some((item) => item.$?.['android:name'] === permission);
80
+ if (!hasPermission) {
81
+ androidManifest['uses-permission']?.push({
82
+ $: { 'android:name': permission },
83
+ });
84
+ }
85
+ });
86
+ }
87
+ // 添加 API Key 到 application 标签
88
+ const mainApplication = androidManifest.application?.[0];
89
+ if (mainApplication && props.androidApiKey) {
90
+ if (!mainApplication['meta-data']) {
91
+ mainApplication['meta-data'] = [];
92
+ }
93
+ // 检查是否已存在
94
+ const hasApiKey = mainApplication['meta-data'].some((item) => item.$?.['android:name'] === 'com.amap.api.v2.apikey');
95
+ if (!hasApiKey) {
96
+ mainApplication['meta-data'].push({
97
+ $: {
98
+ 'android:name': 'com.amap.api.v2.apikey',
99
+ 'android:value': props.androidApiKey,
100
+ },
101
+ });
102
+ }
103
+ else {
104
+ // 更新现有的 API Key
105
+ const apiKeyIndex = mainApplication['meta-data'].findIndex((item) => item.$?.['android:name'] === 'com.amap.api.v2.apikey');
106
+ if (apiKeyIndex !== -1) {
107
+ mainApplication['meta-data'][apiKeyIndex].$ = {
108
+ 'android:name': 'com.amap.api.v2.apikey',
109
+ 'android:value': props.androidApiKey,
110
+ };
111
+ }
112
+ }
113
+ }
114
+ return config;
115
+ });
116
+ };
117
+ /**
118
+ * Android: 修改 app/build.gradle 添加依赖
119
+ */
120
+ const withGaodeMapAppBuildGradle = (config, props) => {
121
+ return (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
122
+ // 这里可以添加额外的 Gradle 配置,如果需要的话
123
+ // 例如添加 maven 仓库或其他依赖
124
+ return config;
125
+ });
126
+ };
127
+ /**
128
+ * 主插件函数 - 组合所有修改器
129
+ */
130
+ const withGaodeMap = (config, props = {}) => {
131
+ // 验证配置
132
+ if (!props.iosApiKey && !props.androidApiKey) {
133
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-gaode-map', '未配置 API Key。请在 app.json 的 plugins 中配置 iosApiKey 和 androidApiKey');
134
+ }
135
+ // 应用 iOS 配置
136
+ config = withGaodeMapInfoPlist(config, props);
137
+ config = withGaodeMapAppDelegate(config, props);
138
+ // 应用 Android 配置
139
+ config = withGaodeMapAndroidManifest(config, props);
140
+ config = withGaodeMapAppBuildGradle(config, props);
141
+ return config;
142
+ };
143
+ /**
144
+ * 导出为可运行一次的插件
145
+ * 这确保插件只会运行一次,即使在配置中被多次引用
146
+ */
147
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(withGaodeMap, 'expo-gaode-map', '1.0.0');
@@ -0,0 +1 @@
1
+ {"root":["./src/withgaodemap.ts"],"version":"5.9.3"}
@@ -243,12 +243,23 @@ declare class ExpoGaodeMapModule extends NativeModule<ExpoGaodeMapModuleEvents>
243
243
  addLocationListener(listener: LocationListener): { remove: () => void };
244
244
  }
245
245
 
246
- // 获取原生模块实例
247
- const nativeModule = requireNativeModule<ExpoGaodeMapModule>('ExpoGaodeMap');
246
+ // 获取原生模块实例 - 添加容错处理
247
+ let nativeModule: ExpoGaodeMapModule | null = null;
248
+
249
+ try {
250
+ nativeModule = requireNativeModule<ExpoGaodeMapModule>('ExpoGaodeMap');
251
+ } catch (error) {
252
+ console.warn('ExpoGaodeMap 原生模块加载失败:', error);
253
+ }
254
+
255
+ // 如果模块加载失败,创建一个空的代理对象防止崩溃
256
+ if (!nativeModule) {
257
+ console.error('ExpoGaodeMap: 原生模块不可用,请检查配置');
258
+ }
248
259
 
249
260
  // 扩展原生模块,添加便捷方法
250
261
  const ExpoGaodeMapModuleWithHelpers = {
251
- ...nativeModule,
262
+ ...(nativeModule || {}),
252
263
 
253
264
  /**
254
265
  * 添加定位监听器(便捷方法)
@@ -259,7 +270,7 @@ const ExpoGaodeMapModuleWithHelpers = {
259
270
  */
260
271
  addLocationListener(listener: LocationListener): { remove: () => void } {
261
272
  // 使用可选链和空值合并,确保即使模块不可用也不会崩溃
262
- return nativeModule.addListener?.('onLocationUpdate', listener) || {
273
+ return nativeModule?.addListener?.('onLocationUpdate', listener) || {
263
274
  remove: () => {},
264
275
  };
265
276
  },