expo-gaode-map 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +19 -3
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +1 -1
  3. package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +24 -6
  4. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +28 -2
  5. package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +6 -3
  6. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +317 -2
  7. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +16 -0
  8. package/android/src/main/java/expo/modules/gaodemap/utils/ColorParser.kt +25 -0
  9. package/build/components/overlays/Circle.d.ts +2 -1
  10. package/build/components/overlays/Circle.d.ts.map +1 -1
  11. package/build/components/overlays/Circle.js +39 -0
  12. package/build/components/overlays/Circle.js.map +1 -1
  13. package/build/components/overlays/Marker.d.ts.map +1 -1
  14. package/build/components/overlays/Marker.js +46 -1
  15. package/build/components/overlays/Marker.js.map +1 -1
  16. package/build/types/location.types.d.ts +4 -0
  17. package/build/types/location.types.d.ts.map +1 -1
  18. package/build/types/location.types.js.map +1 -1
  19. package/build/types/map-view.types.d.ts +2 -1
  20. package/build/types/map-view.types.d.ts.map +1 -1
  21. package/build/types/map-view.types.js.map +1 -1
  22. package/build/types/overlays.types.d.ts +20 -1
  23. package/build/types/overlays.types.d.ts.map +1 -1
  24. package/build/types/overlays.types.js.map +1 -1
  25. package/docs/API.en.md +14 -4
  26. package/docs/API.md +52 -4
  27. package/docs/EXAMPLES.en.md +58 -1
  28. package/docs/EXAMPLES.md +208 -1
  29. package/ios/ExpoGaodeMapView.swift +36 -5
  30. package/ios/ExpoGaodeMapViewModule.swift +1 -1
  31. package/ios/managers/UIManager.swift +32 -4
  32. package/ios/overlays/CircleViewModule.swift +0 -2
  33. package/ios/overlays/MarkerView.swift +205 -7
  34. package/ios/overlays/MarkerViewModule.swift +8 -2
  35. package/ios/overlays/PolygonViewModule.swift +0 -2
  36. package/ios/overlays/PolylineViewModule.swift +0 -2
  37. package/ios/utils/ColorParser.swift +45 -0
  38. package/package.json +3 -2
  39. package/src/components/overlays/Circle.tsx +48 -0
  40. package/src/components/overlays/Marker.tsx +68 -1
  41. package/src/types/location.types.ts +5 -0
  42. package/src/types/map-view.types.ts +2 -1
  43. package/src/types/overlays.types.ts +23 -1
@@ -46,6 +46,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
46
46
  private val onMapPress by EventDispatcher()
47
47
  private val onMapLongPress by EventDispatcher()
48
48
  private val onLoad by EventDispatcher()
49
+ private val onLocation by EventDispatcher()
49
50
  private val onMarkerPress by EventDispatcher()
50
51
  private val onMarkerDragStart by EventDispatcher()
51
52
  private val onMarkerDrag by EventDispatcher()
@@ -80,7 +81,17 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
80
81
 
81
82
  // 初始化管理器
82
83
  cameraManager = CameraManager(aMap)
83
- uiManager = UIManager(aMap, context)
84
+ uiManager = UIManager(aMap, context).apply {
85
+ // 设置定位变化回调
86
+ onLocationChanged = { latitude, longitude, accuracy ->
87
+ this@ExpoGaodeMapView.onLocation(mapOf(
88
+ "latitude" to latitude,
89
+ "longitude" to longitude,
90
+ "accuracy" to accuracy.toDouble(),
91
+ "timestamp" to System.currentTimeMillis()
92
+ ))
93
+ }
94
+ }
84
95
  overlayManager = OverlayManager(aMap, context).apply {
85
96
  onMarkerPress = { id, lat, lng ->
86
97
  this@ExpoGaodeMapView.onMarkerPress(mapOf(
@@ -479,12 +490,17 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
479
490
  * 添加子视图时自动连接到地图
480
491
  */
481
492
  override fun addView(child: View?, index: Int) {
493
+ if (child is MarkerView) {
494
+ // 不添加到视图层级,只调用 setMap
495
+ child.setMap(aMap)
496
+ return
497
+ }
498
+
482
499
  super.addView(child, index)
483
500
 
484
- // 自动将地图实例传递给覆盖物子视图
501
+ // 自动将地图实例传递给其他覆盖物子视图
485
502
  child?.let {
486
503
  when (it) {
487
- is MarkerView -> it.setMap(aMap)
488
504
  is PolylineView -> it.setMap(aMap)
489
505
  is PolygonView -> it.setMap(aMap)
490
506
  is CircleView -> it.setMap(aMap)
@@ -11,7 +11,7 @@ class ExpoGaodeMapViewModule : Module() {
11
11
  Name("ExpoGaodeMapView")
12
12
 
13
13
  View(ExpoGaodeMapView::class) {
14
- Events("onMapPress", "onMapLongPress", "onLoad", "onMarkerPress", "onMarkerDragStart", "onMarkerDrag", "onMarkerDragEnd", "onCirclePress", "onPolygonPress", "onPolylinePress")
14
+ Events("onMapPress", "onMapLongPress", "onLoad", "onLocation", "onMarkerPress", "onMarkerDragStart", "onMarkerDrag", "onMarkerDragEnd", "onCirclePress", "onPolygonPress", "onPolylinePress")
15
15
 
16
16
  Prop<Int>("mapType") { view, type ->
17
17
  view.mapType = type
@@ -65,12 +65,15 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
65
65
  val lng = center["longitude"] ?: 0.0
66
66
  val latLng = LatLng(lat, lng)
67
67
 
68
+ // 将 dp 转换为 px,与 iOS 的 points 对应
69
+ val density = context.resources.displayMetrics.density
70
+
68
71
  val options = com.amap.api.maps.model.CircleOptions()
69
72
  .center(latLng)
70
73
  .radius(radius)
71
74
  .fillColor(fillColor)
72
75
  .strokeColor(strokeColor)
73
- .strokeWidth(strokeWidth)
76
+ .strokeWidth(strokeWidth * density)
74
77
 
75
78
  val circle = aMap.addCircle(options)
76
79
  circles[id] = circle
@@ -104,7 +107,10 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
104
107
  radius?.let { circle.radius = it }
105
108
  fillColor?.let { circle.fillColor = it }
106
109
  strokeColor?.let { circle.strokeColor = it }
107
- strokeWidth?.let { circle.strokeWidth = it }
110
+ strokeWidth?.let {
111
+ val density = context.resources.displayMetrics.density
112
+ circle.strokeWidth = it * density
113
+ }
108
114
  }
109
115
  }
110
116
 
@@ -322,9 +328,12 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
322
328
  LatLng(lat, lng)
323
329
  }
324
330
 
331
+ // 将 dp 转换为 px,与 iOS 的 points 对应
332
+ val density = context.resources.displayMetrics.density
333
+
325
334
  val options = com.amap.api.maps.model.PolylineOptions()
326
335
  .addAll(latLngs)
327
- .width(width)
336
+ .width(width * density)
328
337
  .color(color)
329
338
 
330
339
  val polyline = aMap.addPolyline(options)
@@ -381,7 +390,10 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
381
390
  polyline.points = latLngs
382
391
  }
383
392
 
384
- width?.let { polyline.width = it }
393
+ width?.let {
394
+ val density = context.resources.displayMetrics.density
395
+ polyline.width = it * density
396
+ }
385
397
  color?.let { polyline.color = it }
386
398
  }
387
399
  }
@@ -405,11 +417,14 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
405
417
  LatLng(lat, lng)
406
418
  }
407
419
 
420
+ // 将 dp 转换为 px,与 iOS 的 points 对应
421
+ val density = context.resources.displayMetrics.density
422
+
408
423
  val options = com.amap.api.maps.model.PolygonOptions()
409
424
  .addAll(latLngs)
410
425
  .fillColor(fillColor)
411
426
  .strokeColor(strokeColor)
412
- .strokeWidth(strokeWidth)
427
+ .strokeWidth(strokeWidth * density)
413
428
  .zIndex(zIndex)
414
429
 
415
430
  val polygon = aMap.addPolygon(options)
@@ -448,7 +463,10 @@ class OverlayManager(private val aMap: AMap, private val context: Context) {
448
463
 
449
464
  fillColor?.let { polygon.fillColor = it }
450
465
  strokeColor?.let { polygon.strokeColor = it }
451
- strokeWidth?.let { polygon.strokeWidth = it }
466
+ strokeWidth?.let {
467
+ val density = context.resources.displayMetrics.density
468
+ polygon.strokeWidth = it * density
469
+ }
452
470
  zIndex?.let { polygon.zIndex = it }
453
471
  }
454
472
  }
@@ -3,6 +3,7 @@ package expo.modules.gaodemap.managers
3
3
  import android.content.Context
4
4
  import android.graphics.BitmapFactory
5
5
  import com.amap.api.maps.AMap
6
+ import com.amap.api.maps.LocationSource
6
7
  import com.amap.api.maps.model.BitmapDescriptorFactory
7
8
  import com.amap.api.maps.model.MyLocationStyle
8
9
  import expo.modules.gaodemap.utils.ColorParser
@@ -15,6 +16,8 @@ import java.net.URL
15
16
  */
16
17
  class UIManager(private val aMap: AMap, private val context: Context) {
17
18
 
19
+ var onLocationChanged: ((latitude: Double, longitude: Double, accuracy: Float) -> Unit)? = null
20
+
18
21
  // ==================== 控件显示 ====================
19
22
 
20
23
  /**
@@ -87,8 +90,27 @@ class UIManager(private val aMap: AMap, private val context: Context) {
87
90
  }
88
91
  currentLocationStyle?.myLocationType(locationType)
89
92
  aMap.myLocationStyle = currentLocationStyle
93
+
94
+ // 设置定位监听
95
+ aMap.setLocationSource(object : LocationSource {
96
+ override fun activate(listener: LocationSource.OnLocationChangedListener?) {
97
+ // 高德地图会自动处理定位,我们只需要监听位置变化
98
+ }
99
+ override fun deactivate() {}
100
+ })
101
+
102
+ // 监听定位变化
103
+ aMap.setOnMyLocationChangeListener { location ->
104
+ onLocationChanged?.invoke(
105
+ location.latitude,
106
+ location.longitude,
107
+ location.accuracy
108
+ )
109
+ }
110
+
90
111
  aMap.isMyLocationEnabled = true
91
112
  } else {
113
+ aMap.setOnMyLocationChangeListener(null)
92
114
  aMap.isMyLocationEnabled = false
93
115
  }
94
116
  }
@@ -120,8 +142,12 @@ class UIManager(private val aMap: AMap, private val context: Context) {
120
142
  }
121
143
 
122
144
  // 是否显示精度圈 (showsAccuracyRing)
123
- (config["showsAccuracyRing"] as? Boolean)?.let {
124
- style.showMyLocation(it)
145
+ (config["showsAccuracyRing"] as? Boolean)?.let { showRing ->
146
+ if (!showRing) {
147
+ // 不显示精度圈,但要显示蓝点
148
+ style.radiusFillColor(android.graphics.Color.TRANSPARENT)
149
+ style.strokeColor(android.graphics.Color.TRANSPARENT)
150
+ }
125
151
  }
126
152
 
127
153
  // 自定义图标 (image)
@@ -71,10 +71,12 @@ class CircleView(context: Context, appContext: AppContext) : ExpoView(context, a
71
71
 
72
72
  /**
73
73
  * 设置边框宽度
74
+ * 将 dp 转换为 px,与 iOS 的 points 对应
74
75
  */
75
76
  fun setStrokeWidth(width: Float) {
76
- strokeWidth = width
77
- circle?.strokeWidth = width
77
+ val density = context.resources.displayMetrics.density
78
+ strokeWidth = width * density
79
+ circle?.strokeWidth = strokeWidth
78
80
  }
79
81
 
80
82
  /**
@@ -92,13 +94,14 @@ class CircleView(context: Context, appContext: AppContext) : ExpoView(context, a
92
94
  val centerPoint = center ?: return
93
95
 
94
96
  if (circle == null) {
97
+ val density = context.resources.displayMetrics.density
95
98
  circle = map.addCircle(
96
99
  CircleOptions()
97
100
  .center(centerPoint)
98
101
  .radius(radius)
99
102
  .fillColor(fillColor)
100
103
  .strokeColor(strokeColor)
101
- .strokeWidth(strokeWidth)
104
+ .strokeWidth(strokeWidth * density)
102
105
  )
103
106
  }
104
107
  }
@@ -4,6 +4,11 @@ import android.annotation.SuppressLint
4
4
  import android.content.Context
5
5
  import android.graphics.Bitmap
6
6
  import android.graphics.Canvas
7
+ import android.graphics.Color
8
+ import android.graphics.Paint
9
+ import android.graphics.Path
10
+ import android.os.Handler
11
+ import android.os.Looper
7
12
  import android.view.View
8
13
  import com.amap.api.maps.AMap
9
14
  import com.amap.api.maps.model.BitmapDescriptorFactory
@@ -16,6 +21,12 @@ import expo.modules.kotlin.views.ExpoView
16
21
 
17
22
  class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
18
23
 
24
+ init {
25
+ // 不可交互,通过父视图定位到屏幕外
26
+ isClickable = false
27
+ isFocusable = false
28
+ }
29
+
19
30
  private val onPress by EventDispatcher()
20
31
  private val onDragStart by EventDispatcher()
21
32
  private val onDrag by EventDispatcher()
@@ -23,14 +34,45 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
23
34
 
24
35
  private var marker: Marker? = null
25
36
  private var aMap: AMap? = null
37
+ private var pendingPosition: LatLng? = null
38
+ private var iconWidth: Int = 0 // 用于自定义图标的宽度
39
+ private var iconHeight: Int = 0 // 用于自定义图标的高度
40
+ private var customViewWidth: Int = 0 // 用于自定义视图(children)的宽度
41
+ private var customViewHeight: Int = 0 // 用于自定义视图(children)的高度
42
+ private val mainHandler = Handler(Looper.getMainLooper())
26
43
 
27
44
  /**
28
45
  * 设置地图实例
29
46
  */
30
47
  @Suppress("unused")
31
48
  fun setMap(map: AMap) {
49
+ android.util.Log.d("MarkerView", "🗺️ setMap 被调用,pendingPosition = $pendingPosition, childCount = $childCount")
32
50
  aMap = map
33
51
  createOrUpdateMarker()
52
+
53
+ // 如果之前已经设置了位置但没有 marker,现在设置位置
54
+ pendingPosition?.let { pos ->
55
+ android.util.Log.d("MarkerView", "✅ 应用待处理的位置: $pos")
56
+ marker?.position = pos
57
+ pendingPosition = null
58
+ }
59
+
60
+ // 如果已经有子视图,触发多次延迟更新确保内容渲染
61
+ if (childCount > 0 && marker != null) {
62
+ android.util.Log.d("MarkerView", "🎨 setMap 后触发延迟更新")
63
+
64
+ // 100ms 后第一次更新
65
+ mainHandler.postDelayed({
66
+ android.util.Log.d("MarkerView", "⏰ setMap 第一次延迟更新(100ms)")
67
+ updateMarkerIcon()
68
+ }, 100)
69
+
70
+ // 300ms 后第二次更新,确保 Text 内容已加载
71
+ mainHandler.postDelayed({
72
+ android.util.Log.d("MarkerView", "⏰ setMap 第二次延迟更新(300ms,确保内容加载)")
73
+ updateMarkerIcon()
74
+ }, 300)
75
+ }
34
76
  }
35
77
 
36
78
  /**
@@ -39,10 +81,25 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
39
81
  fun setPosition(position: Map<String, Double>) {
40
82
  val lat = position["latitude"] ?: return
41
83
  val lng = position["longitude"] ?: return
84
+ val latLng = LatLng(lat, lng)
85
+
86
+ android.util.Log.d("MarkerView", "📍 setPosition 被调用: ($lat, $lng), marker = $marker, aMap = $aMap")
42
87
 
43
88
  marker?.let {
44
- it.position = LatLng(lat, lng)
45
- } ?: createOrUpdateMarker()
89
+ android.util.Log.d("MarkerView", "✅ 更新现有 marker 位置")
90
+ it.position = latLng
91
+ pendingPosition = null
92
+ } ?: run {
93
+ android.util.Log.d("MarkerView", "❌ marker 为 null")
94
+ if (aMap != null) {
95
+ android.util.Log.d("MarkerView", "🔧 aMap 存在,创建新 marker")
96
+ createOrUpdateMarker()
97
+ marker?.position = latLng
98
+ } else {
99
+ android.util.Log.d("MarkerView", "⏳ aMap 为 null,保存位置等待 setMap")
100
+ pendingPosition = latLng
101
+ }
102
+ }
46
103
  }
47
104
 
48
105
  /**
@@ -133,15 +190,53 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
133
190
  marker?.let { it.zIndex = zIndex }
134
191
  }
135
192
 
193
+ /**
194
+ * 设置图标宽度(用于自定义图标 icon 属性)
195
+ */
196
+ fun setIconWidth(width: Int) {
197
+ iconWidth = width
198
+ android.util.Log.d("MarkerView", "📏 设置 iconWidth: $width")
199
+ }
200
+
201
+ /**
202
+ * 设置图标高度(用于自定义图标 icon 属性)
203
+ */
204
+ fun setIconHeight(height: Int) {
205
+ iconHeight = height
206
+ android.util.Log.d("MarkerView", "📏 设置 iconHeight: $height")
207
+ }
208
+
209
+ /**
210
+ * 设置自定义视图宽度(用于 children 属性)
211
+ */
212
+ fun setCustomViewWidth(width: Int) {
213
+ customViewWidth = width
214
+ android.util.Log.d("MarkerView", "📏 设置 customViewWidth: $width")
215
+ }
216
+
217
+ /**
218
+ * 设置自定义视图高度(用于 children 属性)
219
+ */
220
+ fun setCustomViewHeight(height: Int) {
221
+ customViewHeight = height
222
+ android.util.Log.d("MarkerView", "📏 设置 customViewHeight: $height")
223
+ }
224
+
136
225
  /**
137
226
  * 创建或更新标记
138
227
  */
139
228
  private fun createOrUpdateMarker() {
140
229
  aMap?.let { map ->
141
230
  if (marker == null) {
231
+ android.util.Log.d("MarkerView", "🔧 创建新的 marker")
142
232
  val options = MarkerOptions()
143
233
  marker = map.addMarker(options)
144
234
 
235
+ android.util.Log.d("MarkerView", "📌 Marker 已添加到地图,childCount = $childCount")
236
+
237
+ // 不立即更新图标,等待延迟更新(在 addView 和 onLayout 中)
238
+ android.util.Log.d("MarkerView", "⏳ 等待延迟更新图标")
239
+
145
240
  // 设置点击监听
146
241
  map.setOnMarkerClickListener { clickedMarker ->
147
242
  if (clickedMarker == marker) {
@@ -194,6 +289,225 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
194
289
  }
195
290
  }
196
291
 
292
+ /**
293
+ * 创建默认 marker 图标 (红色大头针)
294
+ */
295
+ private fun createDefaultMarkerBitmap(): Bitmap {
296
+ val width = 48
297
+ val height = 72
298
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
299
+ val canvas = Canvas(bitmap)
300
+
301
+ val paint = Paint(Paint.ANTI_ALIAS_FLAG)
302
+ paint.color = Color.parseColor("#FF5252")
303
+ paint.style = Paint.Style.FILL
304
+
305
+ // 绘制圆形顶部
306
+ canvas.drawCircle(width / 2f, width / 2f, width / 2f - 2, paint)
307
+
308
+ // 绘制尖端
309
+ val path = Path()
310
+ path.moveTo(width / 2f, height.toFloat())
311
+ path.lineTo(width / 4f, width / 2f + 10f)
312
+ path.lineTo(3 * width / 4f, width / 2f + 10f)
313
+ path.close()
314
+ canvas.drawPath(path, paint)
315
+
316
+ // 绘制白色边框
317
+ paint.color = Color.WHITE
318
+ paint.style = Paint.Style.STROKE
319
+ paint.strokeWidth = 3f
320
+ canvas.drawCircle(width / 2f, width / 2f, width / 2f - 4, paint)
321
+
322
+ return bitmap
323
+ }
324
+
325
+ /**
326
+ * 将视图转换为 Bitmap
327
+ */
328
+ private fun createBitmapFromView(): Bitmap? {
329
+ if (childCount == 0) {
330
+ android.util.Log.w("MarkerView", "❌ childCount = 0")
331
+ return null
332
+ }
333
+
334
+ return try {
335
+ val childView = getChildAt(0)
336
+ android.util.Log.d("MarkerView", "📦 子视图: $childView")
337
+
338
+ // 获取视图实际测量的尺寸(React Native 已经布局好的)
339
+ val measuredWidth = childView.measuredWidth
340
+ val measuredHeight = childView.measuredHeight
341
+
342
+ android.util.Log.d("MarkerView", "📏 子视图测量尺寸: ${measuredWidth}x${measuredHeight}")
343
+
344
+ // 优先使用已测量的尺寸,其次使用 customViewWidth/customViewHeight(用于 children),最后使用默认值
345
+ // 注意:iconWidth/iconHeight 是用于自定义图标的,不用于 children
346
+ val finalWidth = if (measuredWidth > 0) measuredWidth else (if (customViewWidth > 0) customViewWidth else 240)
347
+ val finalHeight = if (measuredHeight > 0) measuredHeight else (if (customViewHeight > 0) customViewHeight else 80)
348
+
349
+ android.util.Log.d("MarkerView", "📏 最终使用尺寸: ${finalWidth}x${finalHeight} (customViewWidth=$customViewWidth, customViewHeight=$customViewHeight)")
350
+
351
+ // 打印视图层次结构以调试
352
+ if (childView is android.view.ViewGroup) {
353
+ android.util.Log.d("MarkerView", "📦 子视图有 ${childView.childCount} 个子视图:")
354
+ for (i in 0 until childView.childCount) {
355
+ val child = childView.getChildAt(i)
356
+ android.util.Log.d("MarkerView", " └─ 子视图[$i]: ${child.javaClass.simpleName}, 可见性: ${child.visibility}")
357
+ if (child is android.widget.TextView) {
358
+ android.util.Log.d("MarkerView", " 文字: '${child.text}', 颜色: ${Integer.toHexString(child.currentTextColor)}, 大小: ${child.textSize}")
359
+ }
360
+ }
361
+ }
362
+
363
+ if (finalWidth <= 0 || finalHeight <= 0) {
364
+ android.util.Log.w("MarkerView", "❌ 最终尺寸无效: ${finalWidth}x${finalHeight}")
365
+ return null
366
+ }
367
+
368
+ // 如果需要重新测量(尺寸改变了)
369
+ if (measuredWidth != finalWidth || measuredHeight != finalHeight) {
370
+ childView.measure(
371
+ MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY),
372
+ MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)
373
+ )
374
+ childView.layout(0, 0, finalWidth, finalHeight)
375
+ android.util.Log.d("MarkerView", "✅ 子视图已重新测量和布局")
376
+ }
377
+
378
+ // 创建 Bitmap
379
+ val bitmap = Bitmap.createBitmap(finalWidth, finalHeight, Bitmap.Config.ARGB_8888)
380
+ val canvas = Canvas(bitmap)
381
+
382
+ // 设置背景为透明
383
+ canvas.drawColor(android.graphics.Color.TRANSPARENT)
384
+
385
+ // 绘制视图及其所有子视图
386
+ childView.draw(canvas)
387
+ android.util.Log.d("MarkerView", "🎨 Bitmap 已绘制,尺寸: ${bitmap.width}x${bitmap.height}")
388
+
389
+ bitmap
390
+ } catch (e: Exception) {
391
+ android.util.Log.e("MarkerView", "❌ 创建 Bitmap 失败", e)
392
+ e.printStackTrace()
393
+ null
394
+ }
395
+ }
396
+
397
+ /**
398
+ * 创建组合 Bitmap:默认 marker + 自定义内容
399
+ */
400
+ private fun createCombinedBitmap(): Bitmap? {
401
+ android.util.Log.d("MarkerView", "🖼️ createCombinedBitmap 开始")
402
+ val customBitmap = createBitmapFromView()
403
+ if (customBitmap == null) {
404
+ android.util.Log.w("MarkerView", "❌ 自定义 Bitmap 为 null")
405
+ return null
406
+ }
407
+ android.util.Log.d("MarkerView", "✅ 自定义 Bitmap: ${customBitmap.width}x${customBitmap.height}")
408
+
409
+ val markerBitmap = createDefaultMarkerBitmap()
410
+ android.util.Log.d("MarkerView", "✅ 默认 Marker Bitmap: ${markerBitmap.width}x${markerBitmap.height}")
411
+
412
+ // 计算总尺寸:marker 在下,自定义内容在上
413
+ val totalWidth = maxOf(markerBitmap.width, customBitmap.width)
414
+ val totalHeight = markerBitmap.height + customBitmap.height + 10 // 10px 间距
415
+
416
+ android.util.Log.d("MarkerView", "📐 组合尺寸: ${totalWidth}x${totalHeight}")
417
+
418
+ val combinedBitmap = Bitmap.createBitmap(totalWidth, totalHeight, Bitmap.Config.ARGB_8888)
419
+ val canvas = Canvas(combinedBitmap)
420
+
421
+ // 绘制自定义内容在上方
422
+ val customX = (totalWidth - customBitmap.width) / 2f
423
+ canvas.drawBitmap(customBitmap, customX, 0f, null)
424
+ android.util.Log.d("MarkerView", "🎨 已绘制自定义内容在 ($customX, 0)")
425
+
426
+ // 绘制 marker 在下方
427
+ val markerX = (totalWidth - markerBitmap.width) / 2f
428
+ val markerY = customBitmap.height + 10f
429
+ canvas.drawBitmap(markerBitmap, markerX, markerY, null)
430
+ android.util.Log.d("MarkerView", "📍 已绘制 marker 在 ($markerX, $markerY)")
431
+
432
+ return combinedBitmap
433
+ }
434
+
435
+ /**
436
+ * 更新 marker 图标
437
+ */
438
+ private fun updateMarkerIcon() {
439
+ android.util.Log.d("MarkerView", "🔄 updateMarkerIcon 被调用,childCount = $childCount")
440
+
441
+ if (childCount > 0) {
442
+ android.util.Log.d("MarkerView", "🎨 开始创建自定义 Bitmap(仅自定义内容)")
443
+ val customBitmap = createBitmapFromView()
444
+ customBitmap?.let {
445
+ android.util.Log.d("MarkerView", "✅ 自定义 Bitmap 创建成功,尺寸: ${it.width}x${it.height}")
446
+
447
+ marker?.setIcon(BitmapDescriptorFactory.fromBitmap(it))
448
+
449
+ // 设置 anchor 为底部中心,让自定义内容底部对齐地图坐标点
450
+ val anchorX = 0.5f // 水平居中
451
+ val anchorY = 1.0f // 垂直底部
452
+ android.util.Log.d("MarkerView", "🎯 设置 marker anchor: ($anchorX, $anchorY)")
453
+ marker?.setAnchor(anchorX, anchorY)
454
+
455
+ android.util.Log.d("MarkerView", "🎯 图标已设置到 marker")
456
+ } ?: run {
457
+ android.util.Log.w("MarkerView", "❌ 自定义 Bitmap 创建失败")
458
+ marker?.setIcon(BitmapDescriptorFactory.defaultMarker())
459
+ }
460
+ } else {
461
+ android.util.Log.d("MarkerView", "📍 没有子视图,使用默认图标")
462
+ marker?.setIcon(BitmapDescriptorFactory.defaultMarker())
463
+ marker?.setAnchor(0.5f, 1.0f) // 默认 anchor
464
+ }
465
+ }
466
+
467
+
468
+ override fun addView(child: View?, index: Int, params: android.view.ViewGroup.LayoutParams?) {
469
+ android.util.Log.d("MarkerView", "➕ addView 被调用,child = $child")
470
+ super.addView(child, index, params)
471
+
472
+ // 延迟更新图标,等待 React Native 样式和内容渲染
473
+ android.util.Log.d("MarkerView", "✅ 子视图已添加,准备延迟更新,marker=${marker}")
474
+ mainHandler.postDelayed({
475
+ android.util.Log.d("MarkerView", "⏰ 第一次延迟更新图标,marker=${marker}")
476
+ if (marker != null) {
477
+ updateMarkerIcon()
478
+ } else {
479
+ android.util.Log.w("MarkerView", "⚠️ marker 为 null,跳过第一次更新")
480
+ }
481
+ }, 50)
482
+
483
+ mainHandler.postDelayed({
484
+ android.util.Log.d("MarkerView", "⏰ 第二次延迟更新图标(确保内容加载),marker=${marker}")
485
+ if (marker != null) {
486
+ updateMarkerIcon()
487
+ } else {
488
+ android.util.Log.w("MarkerView", "⚠️ marker 为 null,跳过第二次更新")
489
+ }
490
+ }, 150)
491
+ }
492
+
493
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
494
+ super.onLayout(changed, left, top, right, bottom)
495
+ android.util.Log.d("MarkerView", "📐 onLayout: changed=$changed, bounds=(${left},${top},${right},${bottom}), marker=${marker}")
496
+
497
+ // 布局完成后再次尝试更新图标(确保样式已应用)
498
+ if (changed && childCount > 0) {
499
+ android.util.Log.d("MarkerView", "🔄 布局改变,延迟更新图标,marker=${marker}")
500
+ mainHandler.postDelayed({
501
+ android.util.Log.d("MarkerView", "⏰ onLayout 延迟更新执行,marker=${marker}")
502
+ if (marker != null) {
503
+ updateMarkerIcon()
504
+ } else {
505
+ android.util.Log.w("MarkerView", "⚠️ marker 为 null,跳过 onLayout 更新")
506
+ }
507
+ }, 200)
508
+ }
509
+ }
510
+
197
511
  /**
198
512
  * 移除标记
199
513
  */
@@ -207,3 +521,4 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
207
521
  removeMarker()
208
522
  }
209
523
  }
524
+
@@ -44,6 +44,22 @@ class MarkerViewModule : Module() {
44
44
  Prop<Map<String, Float>>("anchor") { view: MarkerView, anchor ->
45
45
  view.setAnchor(anchor)
46
46
  }
47
+
48
+ Prop<Int>("iconWidth") { view: MarkerView, width ->
49
+ view.setIconWidth(width)
50
+ }
51
+
52
+ Prop<Int>("iconHeight") { view: MarkerView, height ->
53
+ view.setIconHeight(height)
54
+ }
55
+
56
+ Prop<Int>("customViewWidth") { view: MarkerView, width ->
57
+ view.setCustomViewWidth(width)
58
+ }
59
+
60
+ Prop<Int>("customViewHeight") { view: MarkerView, height ->
61
+ view.setCustomViewHeight(height)
62
+ }
47
63
  }
48
64
  }
49
65
  }
@@ -23,6 +23,8 @@ object ColorParser {
23
23
  return try {
24
24
  when {
25
25
  color.startsWith("#") -> Color.parseColor(color)
26
+ color.startsWith("rgba(") -> parseRgbaColor(color)
27
+ color.startsWith("rgb(") -> parseRgbColor(color)
26
28
  else -> getNamedColor(color)
27
29
  }
28
30
  } catch (e: Exception) {
@@ -30,6 +32,29 @@ object ColorParser {
30
32
  }
31
33
  }
32
34
 
35
+ private fun parseRgbaColor(color: String): Int {
36
+ val values = color.substringAfter("rgba(").substringBefore(")").split(",").map { it.trim() }
37
+ if (values.size != 4) return Color.BLACK
38
+
39
+ val r = values[0].toIntOrNull() ?: return Color.BLACK
40
+ val g = values[1].toIntOrNull() ?: return Color.BLACK
41
+ val b = values[2].toIntOrNull() ?: return Color.BLACK
42
+ val a = (values[3].toFloatOrNull()?.times(255))?.toInt() ?: return Color.BLACK
43
+
44
+ return Color.argb(a, r, g, b)
45
+ }
46
+
47
+ private fun parseRgbColor(color: String): Int {
48
+ val values = color.substringAfter("rgb(").substringBefore(")").split(",").map { it.trim() }
49
+ if (values.size != 3) return Color.BLACK
50
+
51
+ val r = values[0].toIntOrNull() ?: return Color.BLACK
52
+ val g = values[1].toIntOrNull() ?: return Color.BLACK
53
+ val b = values[2].toIntOrNull() ?: return Color.BLACK
54
+
55
+ return Color.rgb(r, g, b)
56
+ }
57
+
33
58
  private fun getNamedColor(name: String): Int {
34
59
  return when (name.lowercase()) {
35
60
  "red" -> Color.RED
@@ -1,3 +1,4 @@
1
+ import * as React from 'react';
1
2
  import type { CircleProps } from '../../types';
2
- export default function Circle(props: CircleProps): null;
3
+ export default function Circle(props: CircleProps): React.JSX.Element;
3
4
  //# sourceMappingURL=Circle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Circle.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Circle.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,WAAW,QA0DhD"}
1
+ {"version":3,"file":"Circle.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Circle.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,EAAE,WAAW,qBAOhD"}