expo-gaode-map 2.2.30-next.0 → 2.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +4 -2
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +117 -57
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +8 -15
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +20 -6
- package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +24 -13
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +351 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +94 -310
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
- package/build/ExpoGaodeMapModule.d.ts +13 -5
- package/build/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapModule.js +166 -34
- package/build/ExpoGaodeMapModule.js.map +1 -1
- package/build/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/ExpoGaodeMapView.js +12 -0
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/AreaMaskOverlay.d.ts +5 -0
- package/build/components/AreaMaskOverlay.d.ts.map +1 -0
- package/build/components/AreaMaskOverlay.js +20 -0
- package/build/components/AreaMaskOverlay.js.map +1 -0
- package/build/components/FoldableMapView.d.ts.map +1 -1
- package/build/components/FoldableMapView.js +115 -104
- package/build/components/FoldableMapView.js.map +1 -1
- package/build/components/RouteOverlay.d.ts +5 -0
- package/build/components/RouteOverlay.d.ts.map +1 -0
- package/build/components/RouteOverlay.js +20 -0
- package/build/components/RouteOverlay.js.map +1 -0
- package/build/components/overlays/Cluster.d.ts.map +1 -1
- package/build/components/overlays/Cluster.js +12 -0
- package/build/components/overlays/Cluster.js.map +1 -1
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +86 -3
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/hooks/useRoutePlayback.d.ts +4 -0
- package/build/hooks/useRoutePlayback.d.ts.map +1 -0
- package/build/hooks/useRoutePlayback.js +310 -0
- package/build/hooks/useRoutePlayback.js.map +1 -0
- package/build/index.d.ts +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/types/common.types.d.ts +29 -5
- package/build/types/common.types.d.ts.map +1 -1
- package/build/types/common.types.js +5 -5
- package/build/types/common.types.js.map +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js.map +1 -1
- package/build/types/location.types.d.ts +28 -0
- package/build/types/location.types.d.ts.map +1 -1
- package/build/types/location.types.js +5 -0
- package/build/types/location.types.js.map +1 -1
- package/build/types/map-view.types.d.ts +22 -22
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +2 -2
- package/build/types/native-module.types.d.ts.map +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/types/overlays.types.d.ts +14 -0
- package/build/types/overlays.types.d.ts.map +1 -1
- package/build/types/overlays.types.js.map +1 -1
- package/build/types/route-playback.types.d.ts +118 -0
- package/build/types/route-playback.types.d.ts.map +1 -0
- package/build/types/route-playback.types.js +2 -0
- package/build/types/route-playback.types.js.map +1 -0
- package/build/utils/RouteUtils.d.ts +8 -0
- package/build/utils/RouteUtils.d.ts.map +1 -0
- package/build/utils/RouteUtils.js +140 -0
- package/build/utils/RouteUtils.js.map +1 -0
- package/ios/ExpoGaodeMapModule.swift +41 -22
- package/ios/ExpoGaodeMapView.swift +236 -241
- package/ios/ExpoGaodeMapViewModule.swift +16 -11
- package/ios/managers/UIManager.swift +5 -4
- package/ios/modules/LocationManager.swift +32 -9
- package/ios/overlays/ClusterView.swift +114 -12
- package/ios/overlays/ClusterViewModule.swift +5 -1
- package/ios/overlays/MarkerView.swift +195 -18
- package/ios/overlays/MarkerViewModule.swift +7 -7
- package/package.json +6 -6
- package/build/utils/throttle.d.ts +0 -10
- package/build/utils/throttle.d.ts.map +0 -1
- package/build/utils/throttle.js +0 -19
- package/build/utils/throttle.js.map +0 -1
|
@@ -4,20 +4,16 @@ import android.annotation.SuppressLint
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.graphics.Bitmap
|
|
6
6
|
import android.graphics.BitmapFactory
|
|
7
|
-
import android.graphics.Canvas
|
|
8
|
-
import android.graphics.Color
|
|
9
7
|
import android.graphics.Rect
|
|
10
|
-
|
|
11
8
|
import android.os.Handler
|
|
12
9
|
import android.os.Looper
|
|
13
10
|
import android.view.View
|
|
11
|
+
import android.view.ViewParent
|
|
14
12
|
import com.amap.api.maps.AMap
|
|
15
|
-
|
|
16
13
|
import com.amap.api.maps.model.BitmapDescriptorFactory
|
|
17
14
|
import com.amap.api.maps.model.LatLng
|
|
18
15
|
import com.amap.api.maps.model.Marker
|
|
19
16
|
import com.amap.api.maps.model.MarkerOptions
|
|
20
|
-
|
|
21
17
|
import com.amap.api.maps.utils.SpatialRelationUtil
|
|
22
18
|
import com.amap.api.maps.utils.overlay.MovingPointOverlay
|
|
23
19
|
import expo.modules.kotlin.AppContext
|
|
@@ -33,8 +29,6 @@ import androidx.core.view.contains
|
|
|
33
29
|
import androidx.core.view.isEmpty
|
|
34
30
|
import androidx.core.graphics.scale
|
|
35
31
|
import android.view.ViewGroup
|
|
36
|
-
import android.widget.ImageView
|
|
37
|
-
import android.widget.TextView
|
|
38
32
|
import com.amap.api.maps.model.animation.AlphaAnimation
|
|
39
33
|
import com.amap.api.maps.model.animation.AnimationSet
|
|
40
34
|
import com.amap.api.maps.model.animation.ScaleAnimation
|
|
@@ -42,13 +36,8 @@ import android.view.animation.DecelerateInterpolator
|
|
|
42
36
|
import expo.modules.gaodemap.companion.BitmapDescriptorCache
|
|
43
37
|
import expo.modules.gaodemap.companion.IconBitmapCache
|
|
44
38
|
import expo.modules.gaodemap.utils.GeometryUtils
|
|
45
|
-
import kotlin.text.StringBuilder
|
|
46
39
|
import kotlin.math.max
|
|
47
40
|
import kotlin.math.min
|
|
48
|
-
|
|
49
|
-
import java.util.concurrent.CountDownLatch
|
|
50
|
-
import java.util.concurrent.TimeUnit
|
|
51
|
-
import androidx.core.graphics.createBitmap
|
|
52
41
|
import expo.modules.gaodemap.utils.LatLngParser
|
|
53
42
|
|
|
54
43
|
class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
@@ -164,7 +153,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
164
153
|
)
|
|
165
154
|
|
|
166
155
|
child.measure(childWidthSpec, childHeightSpec)
|
|
167
|
-
val childBounds = computeContentBounds(child)
|
|
156
|
+
val childBounds = MarkerBitmapRenderer.computeContentBounds(child)
|
|
168
157
|
measuredContentWidth = max(measuredContentWidth, childBounds?.width() ?: child.measuredWidth)
|
|
169
158
|
measuredContentHeight = max(measuredContentHeight, childBounds?.height() ?: child.measuredHeight)
|
|
170
159
|
}
|
|
@@ -486,6 +475,11 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
486
475
|
* JS 端传入稳定的缓存 key
|
|
487
476
|
*/
|
|
488
477
|
fun setCacheKey(key: String?) {
|
|
478
|
+
if (cacheKey == key) {
|
|
479
|
+
return
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
invalidateAppliedCustomMarkerCaches()
|
|
489
483
|
cacheKey = key
|
|
490
484
|
updateMarkerIcon()
|
|
491
485
|
}
|
|
@@ -715,7 +709,15 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
715
709
|
*/
|
|
716
710
|
fun setIconWidth(width: Int) {
|
|
717
711
|
val density = context.resources.displayMetrics.density
|
|
718
|
-
|
|
712
|
+
val resolvedWidth = (width * density).toInt()
|
|
713
|
+
if (iconWidth == resolvedWidth) {
|
|
714
|
+
return
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
iconWidth = resolvedWidth
|
|
718
|
+
pendingIconUri?.let { iconUri ->
|
|
719
|
+
marker?.let { loadAndSetIcon(iconUri, it) }
|
|
720
|
+
}
|
|
719
721
|
}
|
|
720
722
|
|
|
721
723
|
/**
|
|
@@ -724,7 +726,15 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
724
726
|
*/
|
|
725
727
|
fun setIconHeight(height: Int) {
|
|
726
728
|
val density = context.resources.displayMetrics.density
|
|
727
|
-
|
|
729
|
+
val resolvedHeight = (height * density).toInt()
|
|
730
|
+
if (iconHeight == resolvedHeight) {
|
|
731
|
+
return
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
iconHeight = resolvedHeight
|
|
735
|
+
pendingIconUri?.let { iconUri ->
|
|
736
|
+
marker?.let { loadAndSetIcon(iconUri, it) }
|
|
737
|
+
}
|
|
728
738
|
}
|
|
729
739
|
|
|
730
740
|
/**
|
|
@@ -733,7 +743,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
733
743
|
*/
|
|
734
744
|
fun setCustomViewWidth(width: Int) {
|
|
735
745
|
val density = context.resources.displayMetrics.density
|
|
736
|
-
|
|
746
|
+
val resolvedWidth = (width * density).toInt()
|
|
747
|
+
if (customViewWidth == resolvedWidth) {
|
|
748
|
+
return
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
customViewWidth = resolvedWidth
|
|
752
|
+
markCustomMarkerContentDirty()
|
|
737
753
|
}
|
|
738
754
|
|
|
739
755
|
/**
|
|
@@ -742,7 +758,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
742
758
|
*/
|
|
743
759
|
fun setCustomViewHeight(height: Int) {
|
|
744
760
|
val density = context.resources.displayMetrics.density
|
|
745
|
-
|
|
761
|
+
val resolvedHeight = (height * density).toInt()
|
|
762
|
+
if (customViewHeight == resolvedHeight) {
|
|
763
|
+
return
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
customViewHeight = resolvedHeight
|
|
767
|
+
markCustomMarkerContentDirty()
|
|
746
768
|
}
|
|
747
769
|
|
|
748
770
|
/**
|
|
@@ -769,7 +791,6 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
769
791
|
// 只有在没有自定义内容(children)且有 title 或 snippet 时才显示信息窗口
|
|
770
792
|
// 如果有自定义内容,说明用户已经自定义了显示内容,不需要默认信息窗口
|
|
771
793
|
return !(view.isEmpty() && (!marker.title.isNullOrEmpty() || !marker.snippet.isNullOrEmpty()))
|
|
772
|
-
// marker.showInfoWindow()
|
|
773
794
|
}
|
|
774
795
|
return false
|
|
775
796
|
}
|
|
@@ -876,176 +897,17 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
876
897
|
*
|
|
877
898
|
* 注意:render 会在 UI 线程执行;如果当前线程不是 UI 线程,会同步等待 UI 线程完成(有超时)。
|
|
878
899
|
*/
|
|
879
|
-
private fun createBitmapFromView(): Bitmap? {
|
|
900
|
+
private fun createBitmapFromView(snapshot: MarkerBitmapSnapshot? = resolveMarkerBitmapSnapshot()): Bitmap? {
|
|
880
901
|
if (isEmpty()) return null
|
|
881
902
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
val finalWidth = if (measuredWidth > 0) measuredWidth else (if (customViewWidth > 0) customViewWidth else 0)
|
|
892
|
-
val finalHeight = if (measuredHeight > 0) measuredHeight else (if (customViewHeight > 0) customViewHeight else 0)
|
|
893
|
-
|
|
894
|
-
// 🔑 修复:如果尺寸为 0,说明 View 还没准备好,不要生成 Bitmap,否则会导致动画位置偏移
|
|
895
|
-
if (finalWidth <= 0 || finalHeight <= 0) {
|
|
896
|
-
return null
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
val fullCacheKey = "$keyPart|${finalWidth}x${finalHeight}"
|
|
900
|
-
|
|
901
|
-
// 1) 尝试缓存命中
|
|
902
|
-
IconBitmapCache.get(fullCacheKey)?.let { return it }
|
|
903
|
-
|
|
904
|
-
// 2) 未命中,则生成 bitmap(同之前逻辑)
|
|
905
|
-
val bitmap: Bitmap? = if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
906
|
-
renderViewToBitmapInternal(finalWidth, finalHeight)
|
|
907
|
-
} else {
|
|
908
|
-
val latch = CountDownLatch(1)
|
|
909
|
-
var result: Bitmap? = null
|
|
910
|
-
mainHandler.post {
|
|
911
|
-
try {
|
|
912
|
-
result = renderViewToBitmapInternal(finalWidth, finalHeight)
|
|
913
|
-
} finally {
|
|
914
|
-
latch.countDown()
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
try { latch.await(200, TimeUnit.MILLISECONDS) } catch (_: InterruptedException) {}
|
|
918
|
-
result
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
bitmap?.let { IconBitmapCache.put(fullCacheKey, it) }
|
|
922
|
-
return bitmap
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
/**
|
|
927
|
-
* 真正把 view measure/layout/draw 到 Bitmap 的内部方法(必须在主线程调用)
|
|
928
|
-
*/
|
|
929
|
-
private fun renderViewToBitmapInternal(finalWidth: Int, finalHeight: Int): Bitmap? {
|
|
930
|
-
try {
|
|
931
|
-
val childView = if (isNotEmpty()) getChildAt(0) else return null
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
// 🔑 优化:如果 View 尺寸已经符合要求,直接复用现有布局,避免破坏 React Native 的排版
|
|
935
|
-
if (childView.width != finalWidth || childView.height != finalHeight) {
|
|
936
|
-
// 🔑 关键修复:如果子 View 还没完成布局(宽高为 0),不要强行 measure,这会导致布局错乱(如 0x0 -> 252x75)。
|
|
937
|
-
// 直接返回 null,等待下一次 layout(当子 View 准备好时会再次触发)。
|
|
938
|
-
if (childView.width == 0 || childView.height == 0) {
|
|
939
|
-
return null
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// 使用给定的尺寸强制测量布局
|
|
943
|
-
val widthSpec = MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY)
|
|
944
|
-
val heightSpec = MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)
|
|
945
|
-
|
|
946
|
-
// measure + layout
|
|
947
|
-
childView.measure(widthSpec, heightSpec)
|
|
948
|
-
childView.layout(0, 0, finalWidth, finalHeight)
|
|
949
|
-
} else {
|
|
950
|
-
// 如果复用布局,必须检查 left/top 是否为 0。如果不为 0,绘制到 bitmap 时会发生偏移。
|
|
951
|
-
// 很多时候 RN 会给 view 设置 left/top。
|
|
952
|
-
if (childView.left != 0 || childView.top != 0) {
|
|
953
|
-
childView.layout(0, 0, finalWidth, finalHeight)
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// 🔑 修复:创建支持透明度的 bitmap 配置
|
|
958
|
-
val bitmap = createBitmap(finalWidth, finalHeight)
|
|
959
|
-
val canvas = Canvas(bitmap)
|
|
960
|
-
|
|
961
|
-
// 🔑 关键修复:强制启用 view 的绘制缓存,确保内容正确渲染
|
|
962
|
-
childView.isDrawingCacheEnabled = true
|
|
963
|
-
childView.buildDrawingCache(true)
|
|
964
|
-
|
|
965
|
-
// 绘制 view 到 canvas
|
|
966
|
-
childView.draw(canvas)
|
|
967
|
-
|
|
968
|
-
// 清理绘制缓存
|
|
969
|
-
childView.isDrawingCacheEnabled = false
|
|
970
|
-
childView.destroyDrawingCache()
|
|
971
|
-
|
|
972
|
-
val shouldTrimTransparentPadding = customViewWidth <= 0 && customViewHeight <= 0
|
|
973
|
-
return if (shouldTrimTransparentPadding) trimTransparentPadding(bitmap) else bitmap
|
|
974
|
-
} catch (_: Exception) {
|
|
975
|
-
// 遇到异常时返回 null,让上层使用默认图标
|
|
976
|
-
return null
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
private fun trimTransparentPadding(bitmap: Bitmap): Bitmap {
|
|
981
|
-
if (bitmap.width <= 1 || bitmap.height <= 1) {
|
|
982
|
-
return bitmap
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
if (hasOpaquePixelsOnAllBitmapEdges(bitmap)) {
|
|
986
|
-
return bitmap
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
var minX = bitmap.width
|
|
990
|
-
var minY = bitmap.height
|
|
991
|
-
var maxX = -1
|
|
992
|
-
var maxY = -1
|
|
993
|
-
|
|
994
|
-
for (y in 0 until bitmap.height) {
|
|
995
|
-
for (x in 0 until bitmap.width) {
|
|
996
|
-
if (Color.alpha(bitmap.getPixel(x, y)) != 0) {
|
|
997
|
-
if (x < minX) minX = x
|
|
998
|
-
if (y < minY) minY = y
|
|
999
|
-
if (x > maxX) maxX = x
|
|
1000
|
-
if (y > maxY) maxY = y
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
if (maxX < minX || maxY < minY) {
|
|
1006
|
-
return bitmap
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
val trimmedWidth = maxX - minX + 1
|
|
1010
|
-
val trimmedHeight = maxY - minY + 1
|
|
1011
|
-
if (trimmedWidth == bitmap.width && trimmedHeight == bitmap.height) {
|
|
1012
|
-
return bitmap
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
return Bitmap.createBitmap(bitmap, minX, minY, trimmedWidth, trimmedHeight)
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
private fun hasOpaquePixelsOnAllBitmapEdges(bitmap: Bitmap): Boolean {
|
|
1019
|
-
var topEdgeHasPixel = false
|
|
1020
|
-
var bottomEdgeHasPixel = false
|
|
1021
|
-
var leftEdgeHasPixel = false
|
|
1022
|
-
var rightEdgeHasPixel = false
|
|
1023
|
-
|
|
1024
|
-
for (x in 0 until bitmap.width) {
|
|
1025
|
-
if (!topEdgeHasPixel && Color.alpha(bitmap.getPixel(x, 0)) != 0) {
|
|
1026
|
-
topEdgeHasPixel = true
|
|
1027
|
-
}
|
|
1028
|
-
if (!bottomEdgeHasPixel && Color.alpha(bitmap.getPixel(x, bitmap.height - 1)) != 0) {
|
|
1029
|
-
bottomEdgeHasPixel = true
|
|
1030
|
-
}
|
|
1031
|
-
if (topEdgeHasPixel && bottomEdgeHasPixel) {
|
|
1032
|
-
break
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
for (y in 0 until bitmap.height) {
|
|
1037
|
-
if (!leftEdgeHasPixel && Color.alpha(bitmap.getPixel(0, y)) != 0) {
|
|
1038
|
-
leftEdgeHasPixel = true
|
|
1039
|
-
}
|
|
1040
|
-
if (!rightEdgeHasPixel && Color.alpha(bitmap.getPixel(bitmap.width - 1, y)) != 0) {
|
|
1041
|
-
rightEdgeHasPixel = true
|
|
1042
|
-
}
|
|
1043
|
-
if (leftEdgeHasPixel && rightEdgeHasPixel) {
|
|
1044
|
-
break
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
return topEdgeHasPixel && bottomEdgeHasPixel && leftEdgeHasPixel && rightEdgeHasPixel
|
|
903
|
+
val resolvedSnapshot = snapshot ?: return null
|
|
904
|
+
return MarkerBitmapRenderer.createBitmap(
|
|
905
|
+
container = this,
|
|
906
|
+
snapshot = resolvedSnapshot,
|
|
907
|
+
customViewWidth = customViewWidth,
|
|
908
|
+
customViewHeight = customViewHeight,
|
|
909
|
+
mainHandler = mainHandler,
|
|
910
|
+
)
|
|
1049
911
|
}
|
|
1050
912
|
|
|
1051
913
|
/**
|
|
@@ -1064,14 +926,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1064
926
|
return
|
|
1065
927
|
}
|
|
1066
928
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
val
|
|
1074
|
-
val fullCacheKey = "$keyPart|${measuredWidth}x${measuredHeight}"
|
|
929
|
+
val snapshot = resolveMarkerBitmapSnapshot() ?: run {
|
|
930
|
+
if (marker?.isVisible != true) {
|
|
931
|
+
// 自定义 view 还没准备好时,继续等待下一次 layout/update。
|
|
932
|
+
}
|
|
933
|
+
return
|
|
934
|
+
}
|
|
935
|
+
val fullCacheKey = snapshot.fullCacheKey
|
|
1075
936
|
|
|
1076
937
|
// 确定锚点:优先使用用户指定的 pendingAnchor,否则对于自定义 View 使用中心点 (0.5, 0.5)
|
|
1077
938
|
val anchorX = pendingAnchor?.first ?: 0.5f
|
|
@@ -1093,7 +954,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1093
954
|
}
|
|
1094
955
|
|
|
1095
956
|
// 2) Bitmap 缓存命中则生成 Descriptor,或者重新生成
|
|
1096
|
-
val bitmap = IconBitmapCache.get(fullCacheKey) ?: createBitmapFromView() ?: run {
|
|
957
|
+
val bitmap = IconBitmapCache.get(fullCacheKey) ?: createBitmapFromView(snapshot) ?: run {
|
|
1097
958
|
// 🔑 关键修复:如果生成 Bitmap 失败(例如 View 还没准备好)
|
|
1098
959
|
// 不要急着切回默认 Marker,这会导致闪烁和位置跳变。
|
|
1099
960
|
// 只有在 Marker 从未显示过的情况下,才考虑兜底策略。
|
|
@@ -1136,6 +997,24 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1136
997
|
}
|
|
1137
998
|
}
|
|
1138
999
|
|
|
1000
|
+
private fun invalidateAppliedCustomMarkerCaches() {
|
|
1001
|
+
val key = lastAppliedCustomMarkerKey ?: return
|
|
1002
|
+
BitmapDescriptorCache.remove(key)
|
|
1003
|
+
IconBitmapCache.remove(key)
|
|
1004
|
+
lastAppliedCustomMarkerKey = null
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
private fun markCustomMarkerContentDirty(delayMs: Long = 16L) {
|
|
1008
|
+
if (isRemoving || isEmpty()) {
|
|
1009
|
+
return
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
invalidateAppliedCustomMarkerCaches()
|
|
1013
|
+
if (marker != null) {
|
|
1014
|
+
scheduleMarkerIconUpdate(delayMs)
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1139
1018
|
|
|
1140
1019
|
|
|
1141
1020
|
override fun removeView(child: View?) {
|
|
@@ -1156,8 +1035,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1156
1035
|
if (index in 0..<childCount) {
|
|
1157
1036
|
super.removeViewAt(index)
|
|
1158
1037
|
// 只在还有子视图时更新图标
|
|
1159
|
-
if (!isRemoving && childCount >
|
|
1160
|
-
|
|
1038
|
+
if (!isRemoving && childCount > 0 && marker != null) {
|
|
1039
|
+
markCustomMarkerContentDirty(50)
|
|
1161
1040
|
}
|
|
1162
1041
|
// 如果最后一个子视图被移除,什么都不做
|
|
1163
1042
|
// 让 onDetachedFromWindow 处理完整的清理
|
|
@@ -1166,59 +1045,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1166
1045
|
// 忽略异常
|
|
1167
1046
|
}
|
|
1168
1047
|
}
|
|
1169
|
-
private fun
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
return current
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
private fun computeContentBounds(view: View?): Rect? {
|
|
1181
|
-
view ?: return null
|
|
1182
|
-
if (view.visibility != View.VISIBLE) return null
|
|
1183
|
-
|
|
1184
|
-
var resolvedBounds: Rect? = null
|
|
1185
|
-
|
|
1186
|
-
if (view is ViewGroup && view.childCount > 0) {
|
|
1187
|
-
for (i in 0 until view.childCount) {
|
|
1188
|
-
val child = view.getChildAt(i) ?: continue
|
|
1189
|
-
val childBounds = computeContentBounds(child) ?: continue
|
|
1190
|
-
val shiftedBounds = Rect(childBounds)
|
|
1191
|
-
shiftedBounds.offset(child.left, child.top)
|
|
1192
|
-
resolvedBounds = if (resolvedBounds == null) {
|
|
1193
|
-
shiftedBounds
|
|
1194
|
-
} else {
|
|
1195
|
-
Rect(resolvedBounds).apply { union(shiftedBounds) }
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
val hasOwnVisualBounds =
|
|
1201
|
-
view.background != null ||
|
|
1202
|
-
view.paddingLeft != 0 ||
|
|
1203
|
-
view.paddingTop != 0 ||
|
|
1204
|
-
view.paddingRight != 0 ||
|
|
1205
|
-
view.paddingBottom != 0 ||
|
|
1206
|
-
view !is ViewGroup
|
|
1207
|
-
|
|
1208
|
-
val ownWidth = view.measuredWidth.takeIf { it > 0 } ?: view.width
|
|
1209
|
-
val ownHeight = view.measuredHeight.takeIf { it > 0 } ?: view.height
|
|
1210
|
-
|
|
1211
|
-
if (hasOwnVisualBounds && ownWidth > 0 && ownHeight > 0) {
|
|
1212
|
-
val ownBounds = Rect(0, 0, ownWidth, ownHeight)
|
|
1213
|
-
resolvedBounds = if (resolvedBounds == null) {
|
|
1214
|
-
ownBounds
|
|
1215
|
-
} else {
|
|
1216
|
-
Rect(resolvedBounds).apply { union(ownBounds) }
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
return resolvedBounds
|
|
1221
|
-
}
|
|
1048
|
+
private fun resolveMarkerBitmapSnapshot(): MarkerBitmapSnapshot? =
|
|
1049
|
+
MarkerBitmapRenderer.resolveSnapshot(
|
|
1050
|
+
container = this,
|
|
1051
|
+
customViewWidth = customViewWidth,
|
|
1052
|
+
customViewHeight = customViewHeight,
|
|
1053
|
+
cacheKey = cacheKey,
|
|
1054
|
+
)
|
|
1222
1055
|
|
|
1223
1056
|
|
|
1224
1057
|
override fun addView(child: View?, index: Int, params: android.view.ViewGroup.LayoutParams?) {
|
|
@@ -1257,7 +1090,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1257
1090
|
// 🔑 修复:需要延迟更新图标,等待 children 完成布局
|
|
1258
1091
|
// 原因:立即更新会在 children 还未完成测量/布局时就渲染,导致内容为空
|
|
1259
1092
|
if (!isRemoving && marker != null && childCount > childCountBefore) {
|
|
1260
|
-
|
|
1093
|
+
markCustomMarkerContentDirty()
|
|
1261
1094
|
}
|
|
1262
1095
|
}
|
|
1263
1096
|
|
|
@@ -1266,10 +1099,19 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1266
1099
|
// 🔑 修复:布局完成后延迟更新图标
|
|
1267
1100
|
// 即使 changed 为 false,只要有内容,也应该检查是否需要更新(例如子 View 尺寸变化但 MarkerView 没变)
|
|
1268
1101
|
if (!isRemoving && isNotEmpty() && marker != null) {
|
|
1269
|
-
|
|
1102
|
+
markCustomMarkerContentDirty()
|
|
1270
1103
|
}
|
|
1271
1104
|
}
|
|
1272
1105
|
|
|
1106
|
+
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
|
1107
|
+
override fun invalidateChildInParent(location: IntArray?, dirty: Rect?): ViewParent? {
|
|
1108
|
+
val parentRef = super.invalidateChildInParent(location, dirty)
|
|
1109
|
+
if (!isRemoving && isNotEmpty() && marker != null) {
|
|
1110
|
+
markCustomMarkerContentDirty()
|
|
1111
|
+
}
|
|
1112
|
+
return parentRef
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1273
1115
|
|
|
1274
1116
|
|
|
1275
1117
|
/**
|
|
@@ -1517,62 +1359,4 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
|
|
|
1517
1359
|
}
|
|
1518
1360
|
}
|
|
1519
1361
|
|
|
1520
|
-
|
|
1521
|
-
/**
|
|
1522
|
-
* 为 view 和其子树生成一个轻量“指纹”字符串,用作缓存 key。
|
|
1523
|
-
* 注意:这是启发式的,不追求 100% 唯一性,但在大部分自定义 view 场景下能稳定复用。
|
|
1524
|
-
*/
|
|
1525
|
-
fun computeViewFingerprint(view: View?): String {
|
|
1526
|
-
if (view == null) return "null"
|
|
1527
|
-
|
|
1528
|
-
val sb = StringBuilder()
|
|
1529
|
-
// 首先尝试使用开发者可能预设的 tag 或 contentDescription 作为优先标识(稳定且快速)
|
|
1530
|
-
val tag = view.tag
|
|
1531
|
-
if (tag != null) {
|
|
1532
|
-
sb.append("tag=").append(tag.toString()).append(";")
|
|
1533
|
-
return sb.toString()
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
val contentDesc = view.contentDescription
|
|
1537
|
-
if (!contentDesc.isNullOrEmpty()) {
|
|
1538
|
-
sb.append("cdesc=").append(contentDesc.toString()).append(";")
|
|
1539
|
-
return sb.toString()
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
// 否则做一个递归采样:className + 对于 TextView 获取 text + 对于 ImageView 获取 resourceId 或 drawable hash
|
|
1543
|
-
fun appendFor(v: View) {
|
|
1544
|
-
sb.append(v.javaClass.simpleName)
|
|
1545
|
-
when (v) {
|
|
1546
|
-
is TextView -> {
|
|
1547
|
-
val t = v.text?.toString() ?: ""
|
|
1548
|
-
if (t.isNotEmpty()) {
|
|
1549
|
-
sb.append("[text=").append(t).append("]")
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
is ImageView -> {
|
|
1553
|
-
// 尝试读取资源 id(若使用 setImageResource 时可取到),否则取 drawable 的 hashCode 作为近似
|
|
1554
|
-
val resId = v.tag // 开发者可将资源 id 放到 tag 以便稳定识别
|
|
1555
|
-
if (resId is Int && resId != 0) {
|
|
1556
|
-
sb.append("[imgRes=").append(resId).append("]")
|
|
1557
|
-
} else {
|
|
1558
|
-
val dr = v.drawable
|
|
1559
|
-
if (dr != null) {
|
|
1560
|
-
sb.append("[drawableHash=").append(dr.hashCode()).append("]")
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
sb.append(";")
|
|
1566
|
-
if (v is ViewGroup) {
|
|
1567
|
-
for (i in 0 until v.childCount) {
|
|
1568
|
-
val c = v.getChildAt(i)
|
|
1569
|
-
appendFor(c)
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
appendFor(view)
|
|
1575
|
-
// 最终返回一个截断的 sha-like 形式(避免 key 过长)
|
|
1576
|
-
return sb.toString().take(1024)
|
|
1577
|
-
}
|
|
1578
1362
|
}
|
|
@@ -77,9 +77,9 @@ class MarkerViewModule : Module() {
|
|
|
77
77
|
Prop<Int>("customViewHeight") { view, height ->
|
|
78
78
|
view.setCustomViewHeight(height)
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
Prop<String
|
|
82
|
-
|
|
80
|
+
// 缓存key
|
|
81
|
+
Prop<String?>("cacheKey") { view, key ->
|
|
82
|
+
view.setCacheKey(key)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// 平滑移动路径
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LatLng, Coordinates, ReGeocode, LocationListener, LatLngPoint, CoordinateType } from './types';
|
|
1
|
+
import { LatLng, Coordinates, ReGeocode, LocationListener, HeadingListener, LatLngPoint, CoordinateType, GeoLanguage } from './types';
|
|
2
2
|
import type { ExpoGaodeMapModule as NativeExpoGaodeMapModule } from './types/native-module.types';
|
|
3
3
|
import { PrivacyConfig, PrivacyStatus, SDKConfig, PermissionStatus } from './types/common.types';
|
|
4
4
|
declare const helperMethods: {
|
|
@@ -42,6 +42,7 @@ declare const helperMethods: {
|
|
|
42
42
|
isStarted(): Promise<boolean>;
|
|
43
43
|
getCurrentLocation(): Promise<Coordinates | ReGeocode>;
|
|
44
44
|
coordinateConvert(coordinate: LatLngPoint, type: CoordinateType): Promise<LatLng>;
|
|
45
|
+
setGeoLanguage(language: GeoLanguage | string): void;
|
|
45
46
|
setLocatingWithReGeocode(isReGeocode: boolean): void;
|
|
46
47
|
readonly isBackgroundLocationEnabled: boolean;
|
|
47
48
|
/**
|
|
@@ -73,6 +74,13 @@ declare const helperMethods: {
|
|
|
73
74
|
addLocationListener(listener: LocationListener): {
|
|
74
75
|
remove: () => void;
|
|
75
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* 添加方向监听器(iOS)
|
|
79
|
+
* 自动归一化 heading 事件字段,兼容旧版原生返回结构
|
|
80
|
+
*/
|
|
81
|
+
addHeadingListener(listener: HeadingListener): {
|
|
82
|
+
remove: () => void;
|
|
83
|
+
};
|
|
76
84
|
/**
|
|
77
85
|
* 计算两个坐标点之间的距离
|
|
78
86
|
* @param coordinate1 第一个坐标点
|
|
@@ -91,16 +99,16 @@ declare const helperMethods: {
|
|
|
91
99
|
/**
|
|
92
100
|
* 判断点是否在多边形内
|
|
93
101
|
* @param point 要判断的点
|
|
94
|
-
* @param polygon
|
|
102
|
+
* @param polygon 多边形的顶点坐标数组,支持嵌套数组(多边形空洞)
|
|
95
103
|
* @returns 是否在多边形内
|
|
96
104
|
*/
|
|
97
|
-
isPointInPolygon(point: LatLngPoint, polygon: LatLngPoint[]): boolean;
|
|
105
|
+
isPointInPolygon(point: LatLngPoint, polygon: LatLngPoint[] | LatLngPoint[][]): boolean;
|
|
98
106
|
/**
|
|
99
107
|
* 计算多边形面积
|
|
100
|
-
* @param polygon
|
|
108
|
+
* @param polygon 多边形的顶点坐标数组,支持嵌套数组(多边形空洞)
|
|
101
109
|
* @returns 面积(单位:平方米)
|
|
102
110
|
*/
|
|
103
|
-
calculatePolygonArea(polygon: LatLngPoint[]): number;
|
|
111
|
+
calculatePolygonArea(polygon: LatLngPoint[] | LatLngPoint[][]): number;
|
|
104
112
|
/**
|
|
105
113
|
* 计算矩形面积
|
|
106
114
|
* @param southWest 西南角坐标
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,MAAM,EACN,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,
|
|
1
|
+
{"version":3,"file":"ExpoGaodeMapModule.d.ts","sourceRoot":"","sources":["../src/ExpoGaodeMapModule.ts"],"names":[],"mappings":"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"}
|