diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 0000000..6eb797e
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,57 @@
+name: Android CI
+
+on:
+ push:
+ branches:
+ - 'v1.0.0'
+ # master分支不触发工作流
+ - '!master'
+ # dev_开头的分支不触发工作流
+ - '!dev_**'
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Clean with Gradle
+ run: ./gradlew clean
+
+ - name: Build with Gradle
+ run: ./gradlew sample-gaode:assembleRelease
+
+ # 对安装包进行签名
+ - name: Build Signed APK
+ uses: victorbnl/build-signed-apk@main
+ with:
+ keystore_b64: MIIKEQIBAzCCCcoGCSqGSIb3DQEHAaCCCbsEggm3MIIJszCCBaoGCSqGSIb3DQEHAaCCBZsEggWXMIIFkzCCBY8GCyqGSIb3DQEMCgECoIIFQDCCBTwwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFMD9+cALE8NYYZw3IjXoShPZjkkLAgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQnqtq621mwIzaqOBGAeHfOwSCBNA4Pj+jIJ1PQ0ai1LI98EHQoyUXDmKc9oaB7s82c1YQMgBguGKRNabx7wyJsGAaSWif1WWATO2Y/Su8VZAG0wTpuMQVdO9Zq79i47pc2+PwYJLOUTpAqi+A3jO4yehwwk/ABSF39B0HJl51zdvl1sv8/h1hunBShXlVB7j3c8RboGVuQcl4uXEkSwBynwt5VzTtMg36cLRTbw20Ehz1FAmLXk3o/Yp2Uttgsli/waqgInv4p5RvTMLM7qMFC2IEMJfQJaYp72cnhVYX/xSc/nJc9LDYqAVO60oGKdrpjvkCb+o0DLXzXWwCod22L5vqm2iFYL/JVMRBqcFzBB0VAjIf/vkkd3ATFZy/+TVBHWl1AmHvgQQsVtpzhRtoQZbZv2N7ckG+sWNtE5/0tuBeIGp0uc/T2mMwL3HF91jCtuI+WHvH+2WF75di5H5/gQNwdHxo0xx6lEIwn0n/+Z+8RzYHp0Ml04KXxg4ek961mkHdP6+ZVgPDcW1SGnjvOUBDOBKQnBCaMnm6jGs2qv59g0qZw8cvN24FgU7/f+tuwoDWiBKZyd3arajmeYLMkYPN50gOw8oyn6IH0efKzmgpbC0rpicLwtqiXiDbBIDgNSDErdGwESvrwkn1OITEujFFAezBJMgLLpFI3wdCMW1MsVmDEjR1QLdvMRgGYR4ujSX5UnEEObf9IK/t3oaeGF0yaxGoPnAOhZiMgm+Fu/ZVhx0+TJun6ckWRPg8OTf+6/0CA4Jc9jcTPHViSIfAXfXErJMbPGjxiO/KM7C5MZxucVif4DtSmPaGuUs2GipoL275tP44VCDGGk0q087gGCUwP9IhnwoPR/e79WPWJLzaa2Aod9e1xemMYkMC3659TJrJQTFowIAL64i5oCQBmpFAA6gLlcVM4lEB39EiI5k5pxZCIMDx4H/CyiAkGyyWsnjrxTmuiTmIm4H9iARENucTcCRW6xF5x+KF5HKo6bDnQsZeuF40HsJL3mVtL/KbC6kx3aH94i6FXnlejql0PSka/QZEOGtz2yuztUJb6CQN8Q4A0ROJ0aEfJXh6AnyYQO+HGdZpqXxEQt5arzOYur14+dhubo3PNcm38jTB8fwhy4s4BcKy6tRj+0TlreaibEe0py9IENpXSIGqYYPuTLMW+/LJAq/OnnkTDRYvD21nBqH0F7Ays08vgD7175GUjUW8jhXUljXAa5YuCtffl7zSCgxwdAMMH0mZx1a02Xlk89Hwk+wbwVXaJj3gvDbQ7vTDJq62F1ft7UUtGapojbuB9hQU3vJQPs0j6437Qz9vc5rdZbVUXrY+rDgfiT44vwHhf1IKmlMXEa20RstK+Be4wTUH/0dpGKrJkqi/8lUhc3Sx5IKJGiCLrCLMjKQF3u7OVbA2REjO1Ry2Jan17OgmAG2enwkPpnUjSd9bz3H0pe3DV8KjVunXHSMWCE/TSY0HrwMcnbl0vG/qab5MpzkCb7DxLxgG420ykbdxwLn5LAuJEMUxcO8qDL64P3aHzzKcg4N0rR1wUKKtwGkqTo2BrkuHZ1OqLqcLjPXx//Uno+vdB7UESXFsVBk9/h6GYhvuYVVWVyZYK0M2n0U1D2fd8qlzhQpQ4hen7yqVp/sCGieCdAlvjI3Zuh1QSX2Q6AxzDDE8MBcGCSqGSIb3DQEJFDEKHggAawBlAHkAMDAhBgkqhkiG9w0BCRUxFAQSVGltZSAxNjYxNTA0NjUwNjExMIIEAQYJKoZIhvcNAQcGoIID8jCCA+4CAQAwggPnBgkqhkiG9w0BBwEwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFAfv9XktLWXORLV6UPrzymVZYqU0AgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQI+cGoWlzSTyBfNk+AWGF/4CCA3DST2GE7XfjcMQ6xCFh6tdmxcFsLKeL9uaS6lkMs7VvlASfDAqZ0zUKDqq/WTGLbVBbNeOOd5CGVnM/iNEKrs0JUaQcCm5VWnLPcFoWfGBCuhcJWwdIWyo+KJ7TQOMdJ4/iRIsQh6pfmJp4K4H1jUsDod2zkA1GQON8cTUM2dJsj1esgNyv0yxAsAy3998baI0OUq6rIVcEP/3y1X/MVGqOJ8lIXJFkJuGZWobqhBlC/0L+IJ57SIbN9fI5GXHsqJG6Q36meNhgbHFFDRBFIvxVIOG2w/0xkhaKO7huaCce2kTh1Mc4Z26ENP4hnvT89siEiKY1072djHCb2AvO0F2N08L4FjYsuR2jF/DFRDCavGRoB7dRjdIKvBcNRnsxaLKVHkx4hHdtNFBpafcbrzuTO6dGEUCjxTgjz5F0Njvx3gZboDX6NlbYpWdtlqa6EYRB11X13rmm19shx9D/z76W/A4ku4bH7a5WNwbVvh3z9MZLLoWR2SixE9t/PtJsTOKsowhxBMBgI6AXCizAe+HuZpt1Ej8bZy42HqXJhJu1Dsu3WIVArS4qyL+pssIKZWIAxChF9HgAzrmDnpGJng4bn5yXq+HOARE3kFz4Xo4zOqbftDUbqc0ALoImN3eWIj35WkeXSLt7CH7XTQ965pxNM7rK6I6QMn/GLmAudZ38bCfc3xff2bq0lHVe008VjPycVHPe0GjQ2UkrBMVDlp7UwWFYddGmbZYNtJ5alid+XyYb8DNuiMCRguRtFkf5GpK4q2XHFXt58V6CyDFeDbt8NqjahVRhT2S5nRr7YoeXcFy+kGYqxfAIUU/AjRX93KdFoN/wl5pcI78Q3PX7WjwvGVx0Mk2zJ/xIYrjPWBC1a3/p2ftjq9LDwDq0+tnsRKhUO/gYFMvPjJoDm7NHBnwjEHFjhBW7x2PaGCWxD9TpGtbTn6snCt6WkiibbxHyvzpXRURfPoptvegHLHXcng65KD0g7V59/ca1oCfysthPBcSqMlRCClrABQBgFAVKkL+2dQ+12Os9ZZ2m9tGUKhXJTNJL0Po13bnIT8CYu03SlsFOWFXmNC7W/BZlAIlabFTaTcC3Qsxcj2bQHQheuOvlmzMk0oYwmpv9HG4aWOZP4M313OIlBWUtHwdGvLae37eLVK6ZQzlu/bBsxdYq04OoMD4wITAJBgUrDgMCGgUABBTNwDytEwqNH8oTgy5hTbDQ5xpYaQQUF4EU+aFtoDZ/2LCQiAfhFdOH3KECAwGGoA==
+ keystore_password: 123456
+ key_alias: key0
+ key_password: 123456
+
+ # 创建并提交签名后的APK包和源码提交至对应ref_name的tag
+ - name: Create a Release APK
+ uses: ncipollo/release-action@v1
+ with:
+ #artifacts: "sample-baidu/build/outputs/apk/release/*.apk,sample-gaode/build/outputs/apk/release/*.apk,sample-google/build/outputs/apk/release/*.apk,sample-huawei/build/outputs/apk/release/*.apk,sample-tencent/build/outputs/apk/release/*.apk"
+ # v1.0.0 目前只做完了高德地图和腾讯地图
+ artifacts: "sample-gaode/build/outputs/apk/release/*.apk,sample-tencent/build/outputs/apk/release/*.apk"
+ artifactErrorsFailBuild: true
+ #body: "五大地图集成示例APK包以及全量源码"
+ body: "高德地图和腾讯地图示例包APK包以及全量源码"
+ token: ${{ secrets.github_token }}
+ commit: master
+ # tag名称和分支名称保持一致,dev_**分支不触发工作流,vx.x.x分支触发工作流
+ tag: ${{ github.ref_name }}
diff --git a/.github/workflows/sample-gaode.yml b/.github/workflows/sample-gaode.yml
deleted file mode 100644
index 63eb772..0000000
--- a/.github/workflows/sample-gaode.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: Simple-gaode CI
-
-on:
- push:
- branches: [ master ]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- - name: set up JDK 11
- uses: actions/setup-java@v3
- with:
- java-version: '11'
- distribution: 'temurin'
- cache: gradle
-
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
-
- - name: Clean with Gradle
- run: ./gradlew clean
-
- - name: Build with Gradle
- run: ./gradlew sample-gaode:assembleRelease
-
- - name: Build Signed APK
- uses: victorbnl/build-signed-apk@v1.0.0
- with:
- keystore: MIIKEQIBAzCCCcoGCSqGSIb3DQEHAaCCCbsEggm3MIIJszCCBaoGCSqGSIb3DQEHAaCCBZsEggWXMIIFkzCCBY8GCyqGSIb3DQEMCgECoIIFQDCCBTwwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFMD9+cALE8NYYZw3IjXoShPZjkkLAgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQnqtq621mwIzaqOBGAeHfOwSCBNA4Pj+jIJ1PQ0ai1LI98EHQoyUXDmKc9oaB7s82c1YQMgBguGKRNabx7wyJsGAaSWif1WWATO2Y/Su8VZAG0wTpuMQVdO9Zq79i47pc2+PwYJLOUTpAqi+A3jO4yehwwk/ABSF39B0HJl51zdvl1sv8/h1hunBShXlVB7j3c8RboGVuQcl4uXEkSwBynwt5VzTtMg36cLRTbw20Ehz1FAmLXk3o/Yp2Uttgsli/waqgInv4p5RvTMLM7qMFC2IEMJfQJaYp72cnhVYX/xSc/nJc9LDYqAVO60oGKdrpjvkCb+o0DLXzXWwCod22L5vqm2iFYL/JVMRBqcFzBB0VAjIf/vkkd3ATFZy/+TVBHWl1AmHvgQQsVtpzhRtoQZbZv2N7ckG+sWNtE5/0tuBeIGp0uc/T2mMwL3HF91jCtuI+WHvH+2WF75di5H5/gQNwdHxo0xx6lEIwn0n/+Z+8RzYHp0Ml04KXxg4ek961mkHdP6+ZVgPDcW1SGnjvOUBDOBKQnBCaMnm6jGs2qv59g0qZw8cvN24FgU7/f+tuwoDWiBKZyd3arajmeYLMkYPN50gOw8oyn6IH0efKzmgpbC0rpicLwtqiXiDbBIDgNSDErdGwESvrwkn1OITEujFFAezBJMgLLpFI3wdCMW1MsVmDEjR1QLdvMRgGYR4ujSX5UnEEObf9IK/t3oaeGF0yaxGoPnAOhZiMgm+Fu/ZVhx0+TJun6ckWRPg8OTf+6/0CA4Jc9jcTPHViSIfAXfXErJMbPGjxiO/KM7C5MZxucVif4DtSmPaGuUs2GipoL275tP44VCDGGk0q087gGCUwP9IhnwoPR/e79WPWJLzaa2Aod9e1xemMYkMC3659TJrJQTFowIAL64i5oCQBmpFAA6gLlcVM4lEB39EiI5k5pxZCIMDx4H/CyiAkGyyWsnjrxTmuiTmIm4H9iARENucTcCRW6xF5x+KF5HKo6bDnQsZeuF40HsJL3mVtL/KbC6kx3aH94i6FXnlejql0PSka/QZEOGtz2yuztUJb6CQN8Q4A0ROJ0aEfJXh6AnyYQO+HGdZpqXxEQt5arzOYur14+dhubo3PNcm38jTB8fwhy4s4BcKy6tRj+0TlreaibEe0py9IENpXSIGqYYPuTLMW+/LJAq/OnnkTDRYvD21nBqH0F7Ays08vgD7175GUjUW8jhXUljXAa5YuCtffl7zSCgxwdAMMH0mZx1a02Xlk89Hwk+wbwVXaJj3gvDbQ7vTDJq62F1ft7UUtGapojbuB9hQU3vJQPs0j6437Qz9vc5rdZbVUXrY+rDgfiT44vwHhf1IKmlMXEa20RstK+Be4wTUH/0dpGKrJkqi/8lUhc3Sx5IKJGiCLrCLMjKQF3u7OVbA2REjO1Ry2Jan17OgmAG2enwkPpnUjSd9bz3H0pe3DV8KjVunXHSMWCE/TSY0HrwMcnbl0vG/qab5MpzkCb7DxLxgG420ykbdxwLn5LAuJEMUxcO8qDL64P3aHzzKcg4N0rR1wUKKtwGkqTo2BrkuHZ1OqLqcLjPXx//Uno+vdB7UESXFsVBk9/h6GYhvuYVVWVyZYK0M2n0U1D2fd8qlzhQpQ4hen7yqVp/sCGieCdAlvjI3Zuh1QSX2Q6AxzDDE8MBcGCSqGSIb3DQEJFDEKHggAawBlAHkAMDAhBgkqhkiG9w0BCRUxFAQSVGltZSAxNjYxNTA0NjUwNjExMIIEAQYJKoZIhvcNAQcGoIID8jCCA+4CAQAwggPnBgkqhkiG9w0BBwEwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFAfv9XktLWXORLV6UPrzymVZYqU0AgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQI+cGoWlzSTyBfNk+AWGF/4CCA3DST2GE7XfjcMQ6xCFh6tdmxcFsLKeL9uaS6lkMs7VvlASfDAqZ0zUKDqq/WTGLbVBbNeOOd5CGVnM/iNEKrs0JUaQcCm5VWnLPcFoWfGBCuhcJWwdIWyo+KJ7TQOMdJ4/iRIsQh6pfmJp4K4H1jUsDod2zkA1GQON8cTUM2dJsj1esgNyv0yxAsAy3998baI0OUq6rIVcEP/3y1X/MVGqOJ8lIXJFkJuGZWobqhBlC/0L+IJ57SIbN9fI5GXHsqJG6Q36meNhgbHFFDRBFIvxVIOG2w/0xkhaKO7huaCce2kTh1Mc4Z26ENP4hnvT89siEiKY1072djHCb2AvO0F2N08L4FjYsuR2jF/DFRDCavGRoB7dRjdIKvBcNRnsxaLKVHkx4hHdtNFBpafcbrzuTO6dGEUCjxTgjz5F0Njvx3gZboDX6NlbYpWdtlqa6EYRB11X13rmm19shx9D/z76W/A4ku4bH7a5WNwbVvh3z9MZLLoWR2SixE9t/PtJsTOKsowhxBMBgI6AXCizAe+HuZpt1Ej8bZy42HqXJhJu1Dsu3WIVArS4qyL+pssIKZWIAxChF9HgAzrmDnpGJng4bn5yXq+HOARE3kFz4Xo4zOqbftDUbqc0ALoImN3eWIj35WkeXSLt7CH7XTQ965pxNM7rK6I6QMn/GLmAudZ38bCfc3xff2bq0lHVe008VjPycVHPe0GjQ2UkrBMVDlp7UwWFYddGmbZYNtJ5alid+XyYb8DNuiMCRguRtFkf5GpK4q2XHFXt58V6CyDFeDbt8NqjahVRhT2S5nRr7YoeXcFy+kGYqxfAIUU/AjRX93KdFoN/wl5pcI78Q3PX7WjwvGVx0Mk2zJ/xIYrjPWBC1a3/p2ftjq9LDwDq0+tnsRKhUO/gYFMvPjJoDm7NHBnwjEHFjhBW7x2PaGCWxD9TpGtbTn6snCt6WkiibbxHyvzpXRURfPoptvegHLHXcng65KD0g7V59/ca1oCfysthPBcSqMlRCClrABQBgFAVKkL+2dQ+12Os9ZZ2m9tGUKhXJTNJL0Po13bnIT8CYu03SlsFOWFXmNC7W/BZlAIlabFTaTcC3Qsxcj2bQHQheuOvlmzMk0oYwmpv9HG4aWOZP4M313OIlBWUtHwdGvLae37eLVK6ZQzlu/bBsxdYq04OoMD4wITAJBgUrDgMCGgUABBTNwDytEwqNH8oTgy5hTbDQ5xpYaQQUF4EU+aFtoDZ/2LCQiAfhFdOH3KECAwGGoA==
- keystore_password: 123456
- key_alias: key0
- key_password: 123456
- - name: Create a Release APK
- uses: ncipollo/release-action@v1
- with:
- artifacts: "sample-baidu/build/outputs/apk/release/*.apk,sample-gaode/build/outputs/apk/release/*.apk,sample-google/build/outputs/apk/release/*.apk,sample-huawei/build/outputs/apk/release/*.apk,sample-tencent/build/outputs/apk/release/*.apk"
- artifactErrorsFailBuild: true
- body: "五大地图集成示例APK包以及全量源码"
- token: ${{ secrets.github_token }}
- commit: master
- tag: v1.0.${{ github.run_number }}
diff --git a/README.md b/README.md
index 303e315..9d54efc 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,6 @@
OmniMap Compose 🗺
===============
-
-
-
+
Compose一键集成5大地图平台神器:
- ![百度](https://via.placeholder.com/15/4e6ef2/4e6ef2.png) **`百度地图`**
@@ -15,6 +13,8 @@ Compose一键集成5大地图平台神器:
## 注意
5大地图平台,目前只有华为的花瓣地图只支持Android 7.0+,其他平台支持Android 5.0+
+**腾讯地图的缺点**:提工单,客服态度不行,没有高德地图客服好,高德地图解决效率高,腾讯地图基本上客服不会给你解决问题
+
**使用时需注意**:
1.高德地图比例尺控件需要和地图Logo一起作用显示,腾讯地图不需要一起显示,可分开显示
diff --git a/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/ClusterOverlay.kt b/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/ClusterOverlay.kt
index 4438ef5..0c3a919 100644
--- a/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/ClusterOverlay.kt
+++ b/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/ClusterOverlay.kt
@@ -25,6 +25,8 @@ package com.melody.map.gd_compose.overlay
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeNode
import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.platform.LocalContext
import com.amap.api.maps.model.BitmapDescriptor
import com.amap.api.maps.model.Marker
@@ -61,6 +63,7 @@ fun ClusterOverlay(
defaultClusterIcon: BitmapDescriptor,
onClick: (Marker, List)-> Unit
) {
+ val currentOnClick by rememberUpdatedState(newValue = onClick)
val mapApplier = currentComposer.applier as? MapApplier
val context = LocalContext.current
ComposeNode(
@@ -77,7 +80,7 @@ fun ClusterOverlay(
clusterOverlay.setClusterRenderer(clusterRenderer)
clusterOverlay.setOnClusterClickListener(object : ClusterClickListener {
override fun onClick(marker: Marker, clusterItems: List) {
- onClick.invoke(marker,clusterItems)
+ currentOnClick.invoke(marker,clusterItems)
}
})
ClusterOverlayNode(clusterOverlay)
diff --git a/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/Marker.kt b/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/Marker.kt
index 54b836a..d427203 100644
--- a/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/Marker.kt
+++ b/gd-map-compose/src/main/java/com/melody/map/gd_compose/overlay/Marker.kt
@@ -407,11 +407,6 @@ private fun MarkerImpl(
) ?: error("Error adding marker")
marker.`object` = tag
marker.isClickable = isClickable
- marker.setAnimationListener(animationListener)
- marker.setAnimation(animation)
- if(null != animation) {
- marker.startAnimation()
- }
MarkerNode(
compositionContext = compositionContext,
marker = marker,
diff --git a/gd-map-compose/src/main/java/com/melody/map/gd_compose/position/CameraPositionState.kt b/gd-map-compose/src/main/java/com/melody/map/gd_compose/position/CameraPositionState.kt
index 0c83e21..740ccf8 100644
--- a/gd-map-compose/src/main/java/com/melody/map/gd_compose/position/CameraPositionState.kt
+++ b/gd-map-compose/src/main/java/com/melody/map/gd_compose/position/CameraPositionState.kt
@@ -194,7 +194,6 @@ class CameraPositionState(position: CameraPosition = CameraPosition(LatLng(39.91
override fun onMapChangedLocked(newMap: AMap?) {
if (newMap == null) {
// Cancel the animate caller and crash the map setter
- @Suppress("ThrowableNotThrown")
continuation.resumeWithException(CancellationException(
"internal error; no AMap available"))
error(
diff --git a/sample-baidu/src/main/AndroidManifest.xml b/sample-baidu/src/main/AndroidManifest.xml
index 65faf72..b1e2a65 100644
--- a/sample-baidu/src/main/AndroidManifest.xml
+++ b/sample-baidu/src/main/AndroidManifest.xml
@@ -2,8 +2,13 @@
+
+
+
+
variant.outputs.all {
- outputFileName = "sample-gaode_${defaultConfig.versionName}_${formattedDate}-${variant.buildType.name}.apk"
+ outputFileName = "gaode_${defaultConfig.versionName}_${formattedDate}-${variant.buildType.name}.apk"
}
}
}
diff --git a/sample-gaode/src/main/AndroidManifest.xml b/sample-gaode/src/main/AndroidManifest.xml
index 6c35663..32057cd 100644
--- a/sample-gaode/src/main/AndroidManifest.xml
+++ b/sample-gaode/src/main/AndroidManifest.xml
@@ -33,6 +33,7 @@
drivingRoutePlanSearch(startPoint,endPoint)
+ 1 -> busRoutePlanSearch(startPoint,endPoint,cityCode)
+ 2 -> walkRoutePlanSearch(startPoint,endPoint)
+ else -> rideRoutePlanSearch(startPoint,endPoint)
+ }
+ }
+
/**
* 驾车路径规划搜索
*/
- fun drivingRoutePlanSearch(
- startPoint: LatLng,
- endPoint: LatLng,
- routeSearch: RouteSearchV2?,
- listener: RouteSearchV2.OnRouteSearchListener
- ) {
- val newRouteSearch: RouteSearchV2
- if (null == routeSearch) {
- newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
- newRouteSearch.setRouteSearchListener(listener)
- } else {
- newRouteSearch = routeSearch
+ private suspend fun drivingRoutePlanSearch(startPoint: LatLng, endPoint: LatLng): BaseRouteDataState {
+ return suspendCancellableCoroutine { coroutine ->
+ val newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
+ newRouteSearch.setRouteSearchListener(object : RouteSearchV2.OnRouteSearchListener {
+ override fun onDriveRouteSearched(result: DriveRouteResultV2?, errorCode: Int) {
+ if (errorCode == AMapException.CODE_AMAP_SUCCESS) {
+ if (result?.paths != null) {
+ if (result.paths.isNotEmpty()) {
+ coroutine.resumeWith(
+ Result.success(
+ DrivingRouteDataState(
+ routeWidth = 30F,
+ startPos = LatLng(result.startPos.latitude, result.startPos.longitude),
+ targetPos =LatLng(result.targetPos.latitude, result.targetPos.longitude),
+ startMarkerIcon = getStartMarkerIcon(),
+ endMarkerIcon = getEndMarkerIcon(),
+ startGuideIcon = getStartGuideIcon(),
+ endGuideIcon = getEndGuideIcon(),
+ drivePathV2List = result.paths,
+ driveLineSelectedTexture = getDrivingCustomTexture(true),
+ driveLineUnSelectedTexture = getDrivingCustomTexture(false),
+ throughIcon = null,
+ throughPointList = emptyList()
+ )
+ )
+ )
+ return
+ }
+ }
+ coroutine.resumeWith(Result.failure(NullPointerException("没有搜索到相关数据")))
+ } else {
+ coroutine.resumeWith(Result.failure(NullPointerException(errorCode.toString())))
+ }
+ }
+ override fun onBusRouteSearched(p0: BusRouteResultV2?, p1: Int) {
+ }
+ override fun onWalkRouteSearched(p0: WalkRouteResultV2?, p1: Int) {
+ }
+ override fun onRideRouteSearched(p0: RideRouteResultV2?, p1: Int) {
+ }
+ })
+ val fromAndTo = RouteSearchV2.FromAndTo(
+ LatLonPoint(startPoint.latitude, startPoint.longitude),
+ LatLonPoint(endPoint.latitude, endPoint.longitude)
+ )
+ // 第一个参数表示路径规划的起点和终点,第二个参数表示驾车模式,第三个参数表示途经点,第四个参数表示避让区域,第五个参数表示避让道路
+ val query = RouteSearchV2.DriveRouteQuery(
+ fromAndTo,
+ RouteSearchV2.DrivingStrategy.DEFAULT,
+ null,
+ null,
+ ""
+ )
+ // 一定要设置,否则如下的字段都没有数据
+ query.showFields =
+ RouteSearchV2.ShowFields.POLINE or RouteSearchV2.ShowFields.CITIES or RouteSearchV2.ShowFields.COST or RouteSearchV2.ShowFields.NAVI or RouteSearchV2.ShowFields.TMCS
+ // 异步路径规划驾车模式查询
+ newRouteSearch.calculateDriveRouteAsyn(query)
}
- val fromAndTo = RouteSearchV2.FromAndTo(LatLonPoint(startPoint.latitude,startPoint.longitude), LatLonPoint(endPoint.latitude,endPoint.longitude))
- // 第一个参数表示路径规划的起点和终点,第二个参数表示驾车模式,第三个参数表示途经点,第四个参数表示避让区域,第五个参数表示避让道路
- val query = RouteSearchV2.DriveRouteQuery(
- fromAndTo,
- RouteSearchV2.DrivingStrategy.DEFAULT,
- null,
- null,
- ""
- )
- // 一定要设置,否则如下的字段都没有数据
- query.showFields = RouteSearchV2.ShowFields.POLINE or RouteSearchV2.ShowFields.CITIES or RouteSearchV2.ShowFields.COST or RouteSearchV2.ShowFields.NAVI or RouteSearchV2.ShowFields.TMCS
- // 异步路径规划驾车模式查询
- newRouteSearch.calculateDriveRouteAsyn(query)
}
/**
* 公交车路径规划搜索:
* http://a.amap.com/lbs/static/unzip/Android_Map_Doc/Search/com/amap/api/services/route/RouteSearchV2.BusRouteQuery.html
*/
- fun busRoutePlanSearch(
- startPoint: LatLng,
- endPoint: LatLng,
- cityCode: String,
- routeSearch: RouteSearchV2?,
- listener: RouteSearchV2.OnRouteSearchListener
- ) {
- val newRouteSearch: RouteSearchV2
- if (null == routeSearch) {
- newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
- newRouteSearch.setRouteSearchListener(listener)
- } else {
- newRouteSearch = routeSearch
+ private suspend fun busRoutePlanSearch(startPoint: LatLng, endPoint: LatLng, cityCode: String): BaseRouteDataState {
+ return suspendCancellableCoroutine { coroutine ->
+ val newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
+ newRouteSearch.setRouteSearchListener(object : RouteSearchV2.OnRouteSearchListener {
+ override fun onBusRouteSearched(result: BusRouteResultV2?, errorCode: Int) {
+ if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
+ if (result?.paths != null) {
+ if (result.paths.isNotEmpty()) {
+ coroutine.resumeWith(Result.success(
+ BusRouteDataState(
+ routeWidth = 30F,
+ startPos = LatLng(result.startPos.latitude, result.startPos.longitude),
+ targetPos =LatLng(result.targetPos.latitude, result.targetPos.longitude),
+ startMarkerIcon = getStartMarkerIcon(),
+ endMarkerIcon = getEndMarkerIcon(),
+ startGuideIcon = getStartGuideIcon(),
+ endGuideIcon = getEndGuideIcon(),
+ busLineSelectedTexture = getBusCustomTexture(true),
+ busLineUnSelectedTexture = getBusCustomTexture(false),
+ busPathV2List = result.paths
+ )))
+ return
+ }
+ }
+ coroutine.resumeWith(Result.failure(NullPointerException("没有搜索到相关数据")))
+ } else {
+ coroutine.resumeWith(Result.failure(NullPointerException(errorCode.toString())))
+ }
+ }
+ override fun onDriveRouteSearched(p0: DriveRouteResultV2?, p1: Int) {
+ }
+ override fun onWalkRouteSearched(p0: WalkRouteResultV2?, p1: Int) {
+ }
+ override fun onRideRouteSearched(p0: RideRouteResultV2?, p1: Int) {
+ }
+ })
+ val fromAndTo = RouteSearchV2.FromAndTo(
+ LatLonPoint(startPoint.latitude, startPoint.longitude),
+ LatLonPoint(endPoint.latitude, endPoint.longitude)
+ )
+ // 第一个参数:路径的起终点。
+ // 第二个参数:计算路径的模式。可选,默认为最快捷 RouteSearchV2.BusMode。
+ // 第三个参数:城市区号/电话区号。此项不能为空。
+ // 第四个参数:是否计算夜班车,默认为不计算。0:不计算,1:计算。可选。
+ val query = RouteSearchV2.BusRouteQuery(fromAndTo, RouteSearchV2.BusMode.BUS_DEFAULT, cityCode, 1)
+ query.showFields = RouteSearchV2.ShowFields.ALL
+ newRouteSearch.calculateBusRouteAsyn(query)
}
- val fromAndTo = RouteSearchV2.FromAndTo(LatLonPoint(startPoint.latitude,startPoint.longitude), LatLonPoint(endPoint.latitude,endPoint.longitude))
- // 第一个参数:路径的起终点。
- // 第二个参数:计算路径的模式。可选,默认为最快捷 RouteSearchV2.BusMode。
- // 第三个参数:城市区号/电话区号。此项不能为空。
- // 第四个参数:是否计算夜班车,默认为不计算。0:不计算,1:计算。可选。
- val query = RouteSearchV2.BusRouteQuery(fromAndTo, RouteSearchV2.BusMode.BUS_DEFAULT, cityCode, 1)
- query.showFields = RouteSearchV2.ShowFields.ALL
- newRouteSearch.calculateBusRouteAsyn(query)
}
/**
* 步行路径规划搜索
*/
- fun walkRoutePlanSearch(
- startPoint: LatLng,
- endPoint: LatLng,
- routeSearch: RouteSearchV2?,
- listener: RouteSearchV2.OnRouteSearchListener
- ) {
- val newRouteSearch: RouteSearchV2
- if (null == routeSearch) {
- newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
- newRouteSearch.setRouteSearchListener(listener)
- } else {
- newRouteSearch = routeSearch
+ private suspend fun walkRoutePlanSearch(startPoint: LatLng,endPoint: LatLng): BaseRouteDataState {
+ return suspendCancellableCoroutine { coroutine ->
+ val newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
+ newRouteSearch.setRouteSearchListener(object : RouteSearchV2.OnRouteSearchListener {
+ override fun onWalkRouteSearched(result: WalkRouteResultV2?, errorCode: Int) {
+ if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
+ if (result?.paths != null) {
+ if (result.paths.isNotEmpty()) {
+ coroutine.resumeWith(Result.success(
+ WalkRouteDataState(
+ routeWidth = 30F,
+ startPos = LatLng(result.startPos.latitude, result.startPos.longitude),
+ targetPos = LatLng(result.targetPos.latitude, result.targetPos.longitude),
+ startMarkerIcon = getStartMarkerIcon(),
+ endMarkerIcon = getEndMarkerIcon(),
+ startGuideIcon = getStartGuideIcon(),
+ endGuideIcon = getEndGuideIcon(),
+ walkLineSelectedTexture = getBusCustomTexture(true),
+ walkLineUnSelectedTexture = getBusCustomTexture(false),
+ walkNodeIcon = null,
+ walkPathList = result.paths
+ )
+ ))
+ return
+ }
+ }
+ coroutine.resumeWith(Result.failure(NullPointerException("没有搜索到相关数据")))
+ } else {
+ coroutine.resumeWith(Result.failure(NullPointerException(errorCode.toString())))
+ }
+ }
+ override fun onDriveRouteSearched(p0: DriveRouteResultV2?, p1: Int) {
+ }
+ override fun onBusRouteSearched(p0: BusRouteResultV2?, p1: Int) {
+ }
+ override fun onRideRouteSearched(p0: RideRouteResultV2?, p1: Int) {
+ }
+ })
+ val fromAndTo = RouteSearchV2.FromAndTo(
+ LatLonPoint(startPoint.latitude, startPoint.longitude),
+ LatLonPoint(endPoint.latitude, endPoint.longitude)
+ )
+ val query = RouteSearchV2.WalkRouteQuery(fromAndTo)
+ query.showFields = RouteSearchV2.ShowFields.ALL
+ newRouteSearch.calculateWalkRouteAsyn(query)
}
- val fromAndTo = RouteSearchV2.FromAndTo(LatLonPoint(startPoint.latitude,startPoint.longitude), LatLonPoint(endPoint.latitude,endPoint.longitude))
- val query = RouteSearchV2.WalkRouteQuery(fromAndTo)
- query.showFields = RouteSearchV2.ShowFields.ALL
- newRouteSearch.calculateWalkRouteAsyn(query)
}
/**
* 骑行路径规划搜索
*/
- fun rideRoutePlanSearch(
- startPoint: LatLng,
- endPoint: LatLng,
- routeSearch: RouteSearchV2?,
- listener: RouteSearchV2.OnRouteSearchListener
- ) {
- val newRouteSearch: RouteSearchV2
- if (null == routeSearch) {
- newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
- newRouteSearch.setRouteSearchListener(listener)
- } else {
- newRouteSearch = routeSearch
- }
- val fromAndTo = RouteSearchV2.FromAndTo(LatLonPoint(startPoint.latitude,startPoint.longitude), LatLonPoint(endPoint.latitude,endPoint.longitude))
- val query = RouteSearchV2.RideRouteQuery(fromAndTo)
- query.showFields = RouteSearchV2.ShowFields.ALL
- newRouteSearch.calculateRideRouteAsyn(query)
- }
-
- /**
- * 处理驾车路径V2接口搜索结果
- */
- inline fun handleDriveRouteV2Searched(
- result: DriveRouteResultV2?,
- errorCode: Int,
- block: (List?, LatLng?, LatLng?, String?) -> Unit
- ) {
- if (errorCode == AMapException.CODE_AMAP_SUCCESS) {
- if (result?.paths != null) {
- if (result.paths.isNotEmpty()) {
- val startPos = LatLng(result.startPos.latitude, result.startPos.longitude)
- val endPos = LatLng(result.targetPos.latitude, result.targetPos.longitude)
- block.invoke(result.paths, startPos, endPos, null)
- return
+ private suspend fun rideRoutePlanSearch(startPoint: LatLng, endPoint: LatLng): BaseRouteDataState {
+ return suspendCancellableCoroutine { coroutine ->
+ val newRouteSearch = RouteSearchV2(SDKUtils.getApplicationContext())
+ newRouteSearch.setRouteSearchListener(object : RouteSearchV2.OnRouteSearchListener {
+ override fun onRideRouteSearched(result: RideRouteResultV2?, errorCode: Int) {
+ if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
+ if (result?.paths != null) {
+ if (result.paths.isNotEmpty()) {
+ coroutine.resumeWith(Result.success(
+ RideRouteDataState(
+ routeWidth = 30F,
+ startPos = LatLng(result.startPos.latitude, result.startPos.longitude),
+ targetPos = LatLng(result.targetPos.latitude, result.targetPos.longitude),
+ startMarkerIcon = getStartMarkerIcon(),
+ endMarkerIcon = getEndMarkerIcon(),
+ startGuideIcon = getStartGuideIcon(),
+ endGuideIcon = getEndGuideIcon(),
+ rideLineSelectedTexture = getBusCustomTexture(true),
+ rideLineUnSelectedTexture = getBusCustomTexture(false),
+ rideNodeIcon = null,
+ nodeVisible = false,
+ ridePathList = result.paths
+ )
+ ))
+ return
+ }
+ }
+ coroutine.resumeWith(Result.failure(NullPointerException("没有搜索到相关数据")))
+ } else {
+ coroutine.resumeWith(Result.failure(NullPointerException(errorCode.toString())))
+ }
}
- }
- block.invoke(null, null, null, "没有搜索到相关数据")
- } else {
- block.invoke(null, null, null, errorCode.toString())
- }
- }
-
- inline fun handleBusRouteSearched(result: BusRouteResultV2?, errorCode: Int, block: (List?, LatLng?, LatLng?, String?) -> Unit) {
- if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
- if (result?.paths != null) {
- if (result.paths.isNotEmpty()) {
- val startPos = LatLng(result.startPos.latitude, result.startPos.longitude)
- val endPos = LatLng(result.targetPos.latitude, result.targetPos.longitude)
- block.invoke(result.paths,startPos, endPos, null)
- return
+ override fun onDriveRouteSearched(p0: DriveRouteResultV2?, p1: Int) {
}
- }
- block.invoke(null, null, null, "没有搜索到相关数据")
- } else {
- block.invoke(null, null, null, errorCode.toString())
- }
- }
-
- inline fun handleWalkRouteSearched(result: WalkRouteResultV2?, errorCode: Int, block: (List?, LatLng?, LatLng?, String?) -> Unit) {
- if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
- if (result?.paths != null) {
- if (result.paths.isNotEmpty()) {
- val busPath = result.paths[0]
- val startPos = LatLng(result.startPos.latitude, result.startPos.longitude)
- val endPos = LatLng(result.targetPos.latitude, result.targetPos.longitude)
- block.invoke(result.paths,startPos, endPos, null)
- return
+ override fun onBusRouteSearched(p0: BusRouteResultV2?, p1: Int) {
}
- }
- block.invoke(null, null, null, "没有搜索到相关数据")
- } else {
- block.invoke(null, null, null, errorCode.toString())
- }
- }
-
- inline fun handleRideRouteSearched(result: RideRouteResultV2?, errorCode: Int, block: (List?, LatLng?, LatLng?, String?) -> Unit) {
- if(errorCode == AMapException.CODE_AMAP_SUCCESS) {
- if (result?.paths != null) {
- if (result.paths.isNotEmpty()) {
- val startPos = LatLng(result.startPos.latitude, result.startPos.longitude)
- val endPos = LatLng(result.targetPos.latitude, result.targetPos.longitude)
- block.invoke(result.paths,startPos, endPos, null)
- return
+ override fun onWalkRouteSearched(p0: WalkRouteResultV2?, p1: Int) {
}
- }
- block.invoke(null, null, null, "没有搜索到相关数据")
- } else {
- block.invoke(null, null, null, errorCode.toString())
+ })
+ val fromAndTo = RouteSearchV2.FromAndTo(
+ LatLonPoint(startPoint.latitude, startPoint.longitude),
+ LatLonPoint(endPoint.latitude, endPoint.longitude)
+ )
+ val query = RouteSearchV2.RideRouteQuery(fromAndTo)
+ query.showFields = RouteSearchV2.ShowFields.ALL
+ newRouteSearch.calculateRideRouteAsyn(query)
}
}
}
\ No newline at end of file
diff --git a/sample-gaode/src/main/java/com/melody/map/myapplication/viewmodel/RoutePlanViewModel.kt b/sample-gaode/src/main/java/com/melody/map/myapplication/viewmodel/RoutePlanViewModel.kt
index 7337441..50a3bc2 100644
--- a/sample-gaode/src/main/java/com/melody/map/myapplication/viewmodel/RoutePlanViewModel.kt
+++ b/sample-gaode/src/main/java/com/melody/map/myapplication/viewmodel/RoutePlanViewModel.kt
@@ -23,14 +23,10 @@
package com.melody.map.myapplication.viewmodel
import com.amap.api.maps.model.LatLng
-import com.amap.api.services.route.*
import com.melody.map.myapplication.contract.RoutePlanContract
-import com.melody.map.myapplication.model.BusRouteDataState
-import com.melody.map.myapplication.model.DrivingRouteDataState
-import com.melody.map.myapplication.model.RideRouteDataState
-import com.melody.map.myapplication.model.WalkRouteDataState
import com.melody.map.myapplication.repo.RoutePlanRepository
import com.melody.sample.common.base.BaseViewModel
+import kotlinx.coroutines.Dispatchers
/**
* RoutePlanViewModel
@@ -40,10 +36,7 @@ import com.melody.sample.common.base.BaseViewModel
* created 2022/10/14 15:02
*/
class RoutePlanViewModel :
- BaseViewModel(),
- RouteSearchV2.OnRouteSearchListener {
-
- private var routeSearch: RouteSearchV2? = null
+ BaseViewModel(){
override fun createInitialState(): RoutePlanContract.State {
return RoutePlanContract.State(
@@ -63,11 +56,22 @@ class RoutePlanViewModel :
}
is RoutePlanContract.Event.QueryRoutePlan -> {
setState { copy(isLoading = true, dataState = null) }
- when (event.queryType) {
- 0 -> queryDrivingRoutePlan()
- 1 -> queryBusRoutePlan()
- 2 -> queryWalkRoutePlan()
- else -> queryRideRoutePlan()
+ asyncLaunch(Dispatchers.IO) {
+ val result = kotlin.runCatching {
+ RoutePlanRepository.getRoutePlanResult(
+ queryType = event.queryType,
+ startPoint = currentState.queryStartPoint,
+ endPoint = currentState.queryEndPoint,
+ // 北京的城市区号:10
+ cityCode = "10"
+ )
+ }
+ if(result.isSuccess) {
+ setState { copy(isLoading = false, dataState = result.getOrNull()) }
+ } else {
+ setState { copy(isLoading = false) }
+ setEffect { RoutePlanContract.Effect.Toast(result.exceptionOrNull()?.message) }
+ }
}
}
}
@@ -80,163 +84,4 @@ class RoutePlanViewModel :
fun switchRoadTraffic() {
setEvent(RoutePlanContract.Event.RoadTrafficClick)
}
-
- private fun queryDrivingRoutePlan() {
- RoutePlanRepository.drivingRoutePlanSearch(
- startPoint = currentState.queryStartPoint,
- endPoint = currentState.queryEndPoint,
- routeSearch = routeSearch,
- listener = this
- )
- }
-
- private fun queryBusRoutePlan() {
- RoutePlanRepository.busRoutePlanSearch(
- startPoint = currentState.queryStartPoint,
- endPoint = currentState.queryEndPoint,
- // 北京的城市区号:10
- cityCode = "10",
- routeSearch = routeSearch,
- listener = this
- )
- }
-
- private fun queryWalkRoutePlan() {
- RoutePlanRepository.walkRoutePlanSearch(
- startPoint = currentState.queryStartPoint,
- endPoint = currentState.queryEndPoint,
- routeSearch = routeSearch,
- listener = this
- )
- }
-
- private fun queryRideRoutePlan() {
- RoutePlanRepository.rideRoutePlanSearch(
- startPoint = currentState.queryStartPoint,
- endPoint = currentState.queryEndPoint,
- routeSearch = routeSearch,
- listener = this
- )
- }
-
- override fun onDriveRouteSearched(result: DriveRouteResultV2?, errorCode: Int) {
- setState { copy(isLoading = false) }
- RoutePlanRepository.handleDriveRouteV2Searched(
- result,
- errorCode
- ) { drivePathV2, startPos, endPos, message ->
- if (null != drivePathV2) {
- setState {
- copy(
- dataState = DrivingRouteDataState(
- routeWidth = 30F,
- startPos = startPos ?: currentState.queryStartPoint,
- targetPos = endPos ?: currentState.queryEndPoint,
- startMarkerIcon = RoutePlanRepository.getStartMarkerIcon(),
- endMarkerIcon = RoutePlanRepository.getEndMarkerIcon(),
- startGuideIcon = RoutePlanRepository.getStartGuideIcon(),
- endGuideIcon = RoutePlanRepository.getEndGuideIcon(),
- drivePathV2List = drivePathV2,
- driveLineSelectedTexture = RoutePlanRepository.getDrivingCustomTexture(true),
- driveLineUnSelectedTexture = RoutePlanRepository.getDrivingCustomTexture(false),
- throughIcon = null,
- throughPointList = emptyList()
- )
- )
- }
- } else {
- setEffect { RoutePlanContract.Effect.Toast(message) }
- }
- }
- }
-
- override fun onBusRouteSearched(result: BusRouteResultV2?, errorCode: Int) {
- setState { copy(isLoading = false) }
- RoutePlanRepository.handleBusRouteSearched(
- result,
- errorCode
- ) { busPathV2, startPos, endPos, message ->
- if (null != busPathV2) {
- setState {
- copy(
- dataState = BusRouteDataState(
- routeWidth = 30F,
- startPos = startPos ?: currentState.queryStartPoint,
- targetPos = endPos ?: currentState.queryEndPoint,
- startMarkerIcon = RoutePlanRepository.getStartMarkerIcon(),
- endMarkerIcon = RoutePlanRepository.getEndMarkerIcon(),
- startGuideIcon = RoutePlanRepository.getStartGuideIcon(),
- endGuideIcon = RoutePlanRepository.getEndGuideIcon(),
- busLineSelectedTexture = RoutePlanRepository.getBusCustomTexture(true),
- busLineUnSelectedTexture = RoutePlanRepository.getBusCustomTexture(false),
- busPathV2List = busPathV2
- )
- )
- }
- } else {
- setEffect { RoutePlanContract.Effect.Toast(message) }
- }
- }
- }
-
- override fun onWalkRouteSearched(result: WalkRouteResultV2?, errorCode: Int) {
- setState { copy(isLoading = false) }
- RoutePlanRepository.handleWalkRouteSearched(
- result,
- errorCode
- ) { walkPath, startPos, endPos, message ->
- if (null != walkPath) {
- setState {
- copy(
- dataState = WalkRouteDataState(
- routeWidth = 30F,
- startPos = startPos ?: currentState.queryStartPoint,
- targetPos = endPos ?: currentState.queryEndPoint,
- startMarkerIcon = RoutePlanRepository.getStartMarkerIcon(),
- endMarkerIcon = RoutePlanRepository.getEndMarkerIcon(),
- startGuideIcon = RoutePlanRepository.getStartGuideIcon(),
- endGuideIcon = RoutePlanRepository.getEndGuideIcon(),
- walkLineSelectedTexture = RoutePlanRepository.getBusCustomTexture(true),
- walkLineUnSelectedTexture = RoutePlanRepository.getBusCustomTexture(false),
- walkNodeIcon = null,
- walkPathList = walkPath
- )
- )
- }
- } else {
- setEffect { RoutePlanContract.Effect.Toast(message) }
- }
- }
- }
-
- override fun onRideRouteSearched(result: RideRouteResultV2?, errorCode: Int) {
- setState { copy(isLoading = false) }
- RoutePlanRepository.handleRideRouteSearched(
- result,
- errorCode
- ) { ridePath, startPos, endPos, message ->
- if (null != ridePath) {
- setState {
- copy(
- dataState = RideRouteDataState(
- routeWidth = 30F,
- startPos = startPos ?: currentState.queryStartPoint,
- targetPos = endPos ?: currentState.queryEndPoint,
- startMarkerIcon = RoutePlanRepository.getStartMarkerIcon(),
- endMarkerIcon = RoutePlanRepository.getEndMarkerIcon(),
- startGuideIcon = RoutePlanRepository.getStartGuideIcon(),
- endGuideIcon = RoutePlanRepository.getEndGuideIcon(),
- rideLineSelectedTexture = RoutePlanRepository.getBusCustomTexture(true),
- rideLineUnSelectedTexture = RoutePlanRepository.getBusCustomTexture(false),
- rideNodeIcon = null,
- nodeVisible = false,
- ridePathList = ridePath
- )
- )
- }
- } else {
- setEffect { RoutePlanContract.Effect.Toast(message) }
- }
- }
- }
}
\ No newline at end of file
diff --git a/sample-google/src/main/AndroidManifest.xml b/sample-google/src/main/AndroidManifest.xml
index 1744080..c3b2bc1 100644
--- a/sample-google/src/main/AndroidManifest.xml
+++ b/sample-google/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+ tools:ignore="UnusedAttribute">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample-tencent/src/main/assets/MovementTrackTrace.csv b/sample-tencent/src/main/assets/MovementTrackTrace.csv
new file mode 100644
index 0000000..818b080
--- /dev/null
+++ b/sample-tencent/src/main/assets/MovementTrackTrace.csv
@@ -0,0 +1,710 @@
+22.49542833,113.9242475
+22.49547607,113.9242616
+22.49552273,113.9242814
+22.49557834,113.924295
+22.49567166,113.9242944
+22.49570095,113.9243278
+22.49576497,113.9243099
+22.49579915,113.9243248
+22.49588759,113.9242836
+22.49589789,113.924247
+22.49596219,113.924216
+22.49602322,113.924171
+22.49604058,113.9241406
+22.49609429,113.9240875
+22.4961244,113.9240164
+22.49615207,113.9239673
+22.49617106,113.9239195
+22.49617893,113.9238525
+22.49617133,113.9237891
+22.49616564,113.9237242
+22.49612793,113.9236822
+22.49607992,113.9235908
+22.49604736,113.9235688
+22.49600043,113.9235219
+22.49595161,113.9234847
+22.49589572,113.9234405
+22.49583143,113.9234112
+22.49576009,113.9233917
+22.49570964,113.9233865
+22.49564345,113.9233805
+22.49554199,113.9233602
+22.49549506,113.9233599
+22.49543864,113.9233499
+22.49536458,113.923332
+22.49530979,113.9233276
+22.49523872,113.9233
+22.49517822,113.9232804
+22.49513075,113.9232642
+22.49506592,113.9232544
+22.49501004,113.9232349
+22.49495388,113.9232292
+22.49491889,113.923234
+22.49486816,113.9232441
+22.49481689,113.9232642
+22.49476888,113.9232962
+22.49471164,113.9233455
+22.4946601,113.9234001
+22.49461263,113.9234475
+22.49457737,113.9235059
+22.49455322,113.9235845
+22.49453505,113.9236434
+22.49454129,113.9237128
+22.49452908,113.9237853
+22.49454725,113.9238642
+22.4945478,113.9239182
+22.49458035,113.9239676
+22.49461778,113.9240025
+22.49467936,113.9240609
+22.4947366,113.9240869
+22.49479058,113.9241083
+22.49487169,113.9241569
+22.49494086,113.9241737
+22.49500515,113.9242093
+22.49505317,113.9242285
+22.49510851,113.9242247
+22.49517442,113.9242182
+22.49524224,113.9242288
+22.49530382,113.9242291
+22.49535156,113.9242453
+22.49540744,113.9242654
+22.49547879,113.9242868
+22.49553114,113.9243132
+22.49560167,113.924324
+22.49566569,113.9243294
+22.49572537,113.9243126
+22.49580105,113.9242944
+22.49586941,113.9242649
+22.4959375,113.9242095
+22.49602078,113.9241295
+22.49606635,113.9240826
+22.49611328,113.9240004
+22.49615316,113.9239328
+22.49616021,113.9238631
+22.49616618,113.9238045
+22.49616889,113.9237448
+22.49614421,113.9236819
+22.49611898,113.9236195
+22.49608398,113.9235617
+22.49604194,113.9235107
+22.49598389,113.9234703
+22.49587511,113.9234275
+22.49581923,113.9234014
+22.49574761,113.9233822
+22.49569255,113.9233672
+22.49561551,113.923351
+22.49553141,113.9233363
+22.49546821,113.9233211
+22.49541585,113.9233198
+22.4953616,113.9233081
+22.49528998,113.9232926
+22.4952181,113.9232745
+22.49515652,113.9232484
+22.49508328,113.9232378
+22.49502469,113.9232265
+22.49497287,113.9232265
+22.49489529,113.9232414
+22.49481066,113.9232688
+22.49473768,113.9233393
+22.49470025,113.923374
+22.49467421,113.9233624
+22.49464355,113.9234087
+22.49459391,113.9234975
+22.49457764,113.9235639
+22.49455078,113.9236344
+22.49455132,113.9237118
+22.49455811,113.9237763
+22.49455702,113.9238156
+22.49458143,113.9238704
+22.49461561,113.9239204
+22.49466905,113.9240023
+22.49475993,113.9240856
+22.49482964,113.9241162
+22.49491048,113.9241393
+22.49498861,113.9241479
+22.49506429,113.9241621
+22.49514567,113.9241767
+22.49520182,113.9241857
+22.49527452,113.9241979
+22.49534993,113.9241946
+22.49541368,113.9241992
+22.49547879,113.9242144
+22.49555311,113.924232
+22.49562229,113.9242407
+22.4957015,113.9242478
+22.49575168,113.9242711
+22.49580892,113.9242657
+22.49587972,113.9242527
+22.49594048,113.9242185
+22.49599935,113.924174
+22.4960536,113.9241102
+22.49609836,113.9240405
+22.49612766,113.9239716
+22.4961556,113.9238995
+22.49615777,113.9238382
+22.49615343,113.9237497
+22.49612088,113.9236713
+22.49608019,113.9235946
+22.49603651,113.9235352
+22.49598253,113.9234823
+22.49592963,113.9234394
+22.49587402,113.9233987
+22.49579563,113.9233773
+22.49573595,113.9233672
+22.49567952,113.9233588
+22.49561361,113.9233445
+22.49554688,113.9233382
+22.49546875,113.9233241
+22.49540256,113.9233127
+22.49532362,113.9233032
+22.49526015,113.9232924
+22.49518772,113.9232978
+22.49510959,113.923285
+22.49505452,113.9232709
+22.49499051,113.9232669
+22.49492188,113.9232598
+22.49486003,113.9232688
+22.49479085,113.923288
+22.49474935,113.9233279
+22.49469889,113.9233721
+22.49465441,113.9234256
+22.49460422,113.9234977
+22.49458225,113.9235457
+22.49456163,113.9236127
+22.49455594,113.9236727
+22.49451877,113.9237161
+22.49453939,113.9238015
+22.49460015,113.9238832
+22.49462619,113.9239507
+22.49465929,113.9240525
+22.49472602,113.9241235
+22.49476888,113.924145
+22.49481038,113.924184
+22.49486844,113.9241656
+22.49497125,113.9241992
+22.49503933,113.9242057
+22.49510417,113.924228
+22.49517307,113.9242407
+22.49520237,113.924231
+22.49526991,113.9242402
+22.49534722,113.9241981
+22.49540663,113.9242421
+22.49549533,113.9242733
+22.49558485,113.9242735
+22.49564833,113.9242773
+22.49571425,113.9243096
+22.49573541,113.9243153
+22.49580892,113.9242836
+22.49589057,113.9242638
+22.49595947,113.9242266
+22.4960159,113.9241762
+22.49606608,113.9240983
+22.49609728,113.9240069
+22.49611762,113.9239437
+22.49614204,113.9238843
+22.49614204,113.9237942
+22.49613905,113.9237139
+22.49612115,113.9236515
+22.49609077,113.9235848
+22.49604818,113.9235183
+22.49601427,113.9234768
+22.49597005,113.9234416
+22.49589844,113.9234036
+22.49582275,113.9233816
+22.49576172,113.9233735
+22.49570231,113.9233599
+22.49563883,113.9233458
+22.49556912,113.9233344
+22.49549316,113.9233219
+22.49542887,113.9233127
+22.4953635,113.9233035
+22.4952889,113.9232907
+22.49521566,113.923275
+22.49514838,113.9232688
+22.49509087,113.9232631
+22.49501845,113.923249
+22.49497179,113.9232332
+22.49489204,113.9232446
+22.49481988,113.9232837
+22.49474826,113.9233154
+22.49468452,113.9233708
+22.49462267,113.9234361
+22.49459174,113.9234983
+22.4945714,113.9235645
+22.49455675,113.9236456
+22.49455132,113.9237044
+22.49455268,113.9237945
+22.49456434,113.9238612
+22.49457737,113.9239415
+22.49462212,113.9240167
+22.49467448,113.9240663
+22.49473389,113.924107
+22.49478543,113.9241374
+22.49485786,113.9241764
+22.4949311,113.9241881
+22.49501628,113.9242057
+22.49507894,113.9242209
+22.49514459,113.9242364
+22.49519965,113.9242578
+22.49527479,113.9242502
+22.49534587,113.9242608
+22.49542101,113.9242662
+22.49549886,113.9242692
+22.49556261,113.9242776
+22.49563097,113.9242885
+22.49570448,113.9242958
+22.49576606,113.9242839
+22.49584527,113.9242556
+22.49591634,113.9241938
+22.49597358,113.9241488
+22.49602159,113.9240991
+22.49606635,113.9240115
+22.4961084,113.9239657
+22.49612739,113.9238789
+22.49613742,113.9238083
+22.49611789,113.923705
+22.49609755,113.923632
+22.49607151,113.9235506
+22.49604167,113.9235031
+22.49601047,113.9234695
+22.49595296,113.9234296
+22.49589545,113.9233841
+22.49582547,113.9233705
+22.49577121,113.9233678
+22.49571018,113.9233577
+22.49563449,113.9233453
+22.49553792,113.9233225
+22.49545709,113.9233081
+22.49537299,113.9232989
+22.49531277,113.9232967
+22.49519938,113.9233
+22.49514377,113.9232869
+22.49506972,113.9232661
+22.49500109,113.9232701
+22.49495958,113.9232593
+22.4948961,113.9232639
+22.4948329,113.9232932
+22.49473633,113.9233518
+22.49466607,113.9233838
+22.49463406,113.9234318
+22.49457872,113.9235251
+22.49455838,113.9236046
+22.49454915,113.9236754
+22.49455051,113.9237546
+22.49455051,113.923839
+22.49458577,113.9239084
+22.49461019,113.9239491
+22.49464789,113.923992
+22.49468397,113.9240557
+22.49476318,113.9241121
+22.4948112,113.9241382
+22.49487983,113.9241648
+22.49496067,113.9241648
+22.49502821,113.9241919
+22.49511366,113.9242068
+22.49518338,113.9242141
+22.49523926,113.9242337
+22.49532661,113.9242415
+22.49540066,113.9242358
+22.49548069,113.9242605
+22.49557753,113.9242833
+22.495641,113.9242847
+22.49571587,113.9242901
+22.49579861,113.9242806
+22.49588894,113.9242448
+22.49597439,113.9241957
+22.49604031,113.9241379
+22.49609104,113.9240576
+22.4961225,113.9239771
+22.49614746,113.9238943
+22.49614041,113.9237969
+22.49613173,113.9236968
+22.4960867,113.9236003
+22.49602675,113.9235072
+22.49595106,113.9234421
+22.49589193,113.9234033
+22.49584201,113.9233903
+22.49576335,113.9233808
+22.49563829,113.9233743
+22.4955287,113.9233605
+22.49539849,113.9233195
+22.49531359,113.9233184
+22.4952086,113.9233049
+22.49510444,113.9232864
+22.49502252,113.9232642
+22.49491835,113.923252
+22.49481852,113.923281
+22.4947168,113.9233385
+22.49465576,113.9233458
+22.49462348,113.9233824
+22.4945752,113.923476
+22.49455051,113.9235522
+22.49452474,113.9236545
+22.49450439,113.9237408
+22.49453234,113.9238037
+22.4945459,113.9238873
+22.49458523,113.9239315
+22.49462646,113.9239654
+22.49465115,113.9240207
+22.49472982,113.9241059
+22.49477485,113.9241295
+22.49481418,113.924181
+22.49483561,113.9241759
+22.49489068,113.9241968
+22.49492622,113.9242052
+22.49494276,113.9242095
+22.4949924,113.9242082
+22.49503174,113.9242269
+22.49506972,113.9242272
+22.49513075,113.924219
+22.49518039,113.9242136
+22.49523275,113.9242188
+22.49526286,113.9242131
+22.49531928,113.9242174
+22.49536838,113.9242334
+22.495405,113.9242494
+22.49544922,113.924254
+22.49550537,113.9242757
+22.49553304,113.9242944
+22.49555013,113.9242771
+22.49558105,113.9242863
+22.49562174,113.9242923
+22.49566108,113.9242792
+22.49571289,113.924289
+22.49571967,113.9243042
+22.49575846,113.9243145
+22.49578423,113.9243004
+22.49582872,113.924283
+22.49586778,113.9242603
+22.49592014,113.9242356
+22.49598443,113.9241848
+22.49601725,113.9241349
+22.49603678,113.9241127
+22.49607368,113.9240731
+22.49610026,113.9240286
+22.4961263,113.9239836
+22.4961461,113.9239526
+22.49615614,113.9239176
+22.49615614,113.9238723
+22.49615397,113.9238382
+22.49614882,113.9237733
+22.49613797,113.9237457
+22.49613091,113.9236979
+22.49611871,113.9236553
+22.49607883,113.9236152
+22.4960555,113.9235666
+22.49604167,113.9235357
+22.4960262,113.9235216
+22.49600911,113.9235029
+22.495979,113.9234608
+22.4958884,113.9234131
+22.49590549,113.9234071
+22.49588026,113.9234012
+22.49581109,113.9233851
+22.49577501,113.9233738
+22.49574517,113.9233596
+22.49570936,113.9233588
+22.49567166,113.9233534
+22.49562609,113.9233694
+22.49556044,113.923358
+22.49551459,113.9233496
+22.4954579,113.9233203
+22.49543728,113.9233143
+22.49542345,113.9233065
+22.49536919,113.9232807
+22.4953201,113.9232753
+22.49527208,113.9232696
+22.49522217,113.9232528
+22.49518907,113.9232579
+22.49515734,113.9232704
+22.49513129,113.9232625
+22.49506049,113.9232539
+22.49503852,113.92324
+22.49502062,113.9231831
+22.49501383,113.9231841
+22.49495795,113.9232121
+22.49492486,113.9232151
+22.49486708,113.9232216
+22.49482883,113.9232593
+22.49477593,113.9232848
+22.49474989,113.9232856
+22.49470567,113.9233404
+22.49467258,113.9233773
+22.4946403,113.9234047
+22.49461561,113.923434
+22.49461019,113.9234595
+22.49458442,113.9235159
+22.49458442,113.923546
+22.49457845,113.923555
+22.49456896,113.9236339
+22.49454942,113.9236233
+22.49453776,113.9237077
+22.49453369,113.9237552
+22.49453179,113.9237676
+22.49454427,113.9237994
+22.49453179,113.9238506
+22.49455187,113.9239019
+22.4945988,113.9239974
+22.49464003,113.9240337
+22.49468235,113.9240546
+22.49471625,113.9240742
+22.49473931,113.9241062
+22.49475939,113.9241135
+22.49480686,113.9241515
+22.49483534,113.924171
+22.49486735,113.9241829
+22.49490967,113.9241914
+22.49492622,113.9241897
+22.49495443,113.9241984
+22.4949783,113.92419
+22.49500814,113.9242017
+22.495051,113.924203
+22.4951161,113.9242212
+22.49517714,113.9242342
+22.49521457,113.9242339
+22.49526557,113.9242282
+22.49528673,113.924231
+22.49531196,113.924247
+22.49535889,113.9242611
+22.49542182,113.9242809
+22.49545247,113.9242839
+22.49549479,113.9242958
+22.4955447,113.9242849
+22.49560818,113.9242841
+22.4956448,113.9242917
+22.49567139,113.9242825
+22.49573649,113.9242828
+22.49577338,113.9242855
+22.49579915,113.9242925
+22.4958195,113.924289
+22.49583632,113.9242896
+22.49588433,113.9242757
+22.49592204,113.9242594
+22.49595703,113.9242228
+22.49598307,113.9242098
+22.49600532,113.9241914
+22.49606744,113.9241086
+22.49609266,113.9240514
+22.49611545,113.9240158
+22.4961301,113.9239776
+22.4961461,113.9239084
+22.49614638,113.9238805
+22.49614339,113.9238292
+22.4961377,113.9237823
+22.49613878,113.9237652
+22.49613607,113.9237302
+22.49612088,113.9236949
+22.49608914,113.9236323
+22.49606391,113.9235758
+22.49603299,113.9235192
+22.49601345,113.9234956
+22.49598145,113.9234964
+22.49598226,113.9234766
+22.49590929,113.9234375
+22.49587457,113.9234093
+22.49580756,113.9233846
+22.49578071,113.923367
+22.4957525,113.9233662
+22.49573785,113.9233651
+22.49570204,113.9233561
+22.49566054,113.9233483
+22.49561985,113.9233325
+22.49559842,113.923323
+22.49554253,113.9233054
+22.49550293,113.9233092
+22.49545654,113.9233073
+22.49537326,113.9233168
+22.49533936,113.9232926
+22.49530653,113.9232869
+22.49527805,113.9232794
+22.49526638,113.9232845
+22.49523655,113.9232747
+22.49518528,113.9232696
+22.4951359,113.9232633
+22.49507297,113.9232563
+22.49502333,113.9232503
+22.49496826,113.9232452
+22.4949094,113.9232416
+22.49486192,113.9232414
+22.49486816,113.923253
+22.49485107,113.923259
+22.49480849,113.9232867
+22.49478895,113.9232924
+22.49472331,113.9233233
+22.49467909,113.9233515
+22.49462538,113.9233952
+22.49461833,113.9234239
+22.49460286,113.9234668
+22.49457357,113.923504
+22.49456353,113.9235232
+22.49456624,113.9235522
+22.49453396,113.9235967
+22.49455729,113.9236605
+22.49454074,113.9236865
+22.49453396,113.9237242
+22.49453261,113.9237668
+22.49453369,113.9238159
+22.49453478,113.9238482
+22.49454183,113.9238759
+22.494544,113.9239095
+22.49455648,113.9239366
+22.49457791,113.9239903
+22.49458794,113.9240215
+22.49460368,113.9240582
+22.49461697,113.9240799
+22.49466173,113.9241214
+22.49473199,113.9241827
+22.49478461,113.9241623
+22.49484158,113.9241762
+22.49489583,113.9241965
+22.49493652,113.924206
+22.4949943,113.9242152
+22.49506429,113.9242098
+22.49509223,113.9242125
+22.49512885,113.9242155
+22.4951652,113.9242139
+22.49520264,113.9242174
+22.49524902,113.9242228
+22.49525391,113.9242209
+22.49529921,113.924222
+22.49534939,113.9242304
+22.49537869,113.9242285
+22.49542291,113.9242489
+22.49544027,113.9242641
+22.49546522,113.9242589
+22.49554742,113.9242657
+22.49553847,113.9242971
+22.49556586,113.9243107
+22.49562419,113.9243259
+22.49568305,113.9243207
+22.49570475,113.9243229
+22.49572781,113.9243259
+22.49574951,113.9243132
+22.49578559,113.9243142
+22.49583388,113.9242977
+22.49587619,113.9242749
+22.49592584,113.9242513
+22.4959668,113.9242188
+22.49599609,113.9242017
+22.49604302,113.9241604
+22.4960574,113.9241214
+22.49608832,113.9240845
+22.49610867,113.9240422
+22.49611491,113.9239985
+22.49614285,113.9239819
+22.4961518,113.9239109
+22.49616157,113.9238702
+22.4961697,113.9238186
+22.49615777,113.9237869
+22.49615723,113.923731
+22.49614041,113.9237109
+22.49614014,113.9236833
+22.49612305,113.9236496
+22.4961084,113.9236114
+22.49606038,113.9235419
+22.49605794,113.9235102
+22.49601535,113.9235002
+22.49598117,113.9234603
+22.49594537,113.9234402
+22.49591905,113.9233887
+22.49586778,113.9233558
+22.49581679,113.9233521
+22.49576742,113.9233621
+22.49575222,113.9233466
+22.49571343,113.9233426
+22.49567654,113.9233407
+22.49562717,113.9233306
+22.49557834,113.9233249
+22.49554579,113.9233586
+22.4954975,113.9233865
+22.49544162,113.923383
+22.49540663,113.9233694
+22.49534587,113.9233485
+22.49530056,113.9233268
+22.49526015,113.923329
+22.49520752,113.9233219
+22.49517497,113.9233127
+22.49512858,113.923335
+22.49509684,113.9233314
+22.49505941,113.9232981
+22.49501112,113.9232742
+22.49497586,113.9232568
+22.49490831,113.9232614
+22.49485487,113.9232636
+22.49482042,113.9232696
+22.49479085,113.9232905
+22.4947404,113.9233211
+22.49469537,113.92337
+22.49468669,113.9233832
+22.49464952,113.9234229
+22.49463759,113.9234497
+22.49460341,113.9235026
+22.49459988,113.9235395
+22.49458767,113.9235805
+22.49458605,113.9236312
+22.49458279,113.9236608
+22.4945912,113.9237131
+22.49459717,113.9237473
+22.4946091,113.9238005
+22.49459988,113.9238371
+22.49465413,113.9239564
+22.49469672,113.923976
+22.49469699,113.9240161
+22.49471707,113.9240283
+22.49474121,113.9240641
+22.49477322,113.9240861
+22.49482775,113.9241097
+22.49488227,113.9241252
+22.49493652,113.9241534
+22.49499105,113.9241783
+22.49506049,113.9241686
+22.49511393,113.9241683
+22.49515218,113.9241753
+22.49518283,113.9241786
+22.49521322,113.9241843
+22.49527751,113.9241922
+22.49532118,113.9241819
+22.49536838,113.9242038
+22.49538357,113.9242057
+22.49542101,113.9242179
+22.49546549,113.9242285
+22.49549533,113.9242326
+22.49552951,113.9242353
+22.49555827,113.9242369
+22.49560113,113.9242529
+22.49564263,113.9242556
+22.49565782,113.9242575
+22.49572971,113.9242684
+22.49578152,113.9242754
+22.49583442,113.924266
+22.49588243,113.9242616
+22.49590658,113.9242573
+22.49592855,113.924241
+22.49595893,113.9242198
+22.49596436,113.9241729
+22.4960121,113.9241252
+22.49603516,113.9240622
+22.49606147,113.9240335
+22.49608914,113.9239456
+22.49609646,113.9238981
+22.49610704,113.9238523
+22.49611627,113.9238146
+22.49611871,113.9237804
+22.49611464,113.9237465
+22.49610352,113.9237183
+22.49608019,113.9236713
+22.49607151,113.9236464
+22.49601807,113.9235601
+22.49599392,113.9235273
+22.49591688,113.9234494
+22.49590034,113.923454
+22.49586127,113.9234394
+22.4958176,113.9234123
+22.49578369,113.9233919
+22.49575277,113.9233841
+22.49572211,113.9233724
+22.49570014,113.9233759
+22.49563558,113.9233686
+22.49556939,113.9233542
+22.49553602,113.923342
+22.49548584,113.9233398
\ No newline at end of file
diff --git a/sample-tencent/src/main/assets/ic_pdd_car.png b/sample-tencent/src/main/assets/ic_pdd_car.png
new file mode 100644
index 0000000..9816e9c
Binary files /dev/null and b/sample-tencent/src/main/assets/ic_pdd_car.png differ
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/DragDropSelectPointActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/DragDropSelectPointActivity.kt
new file mode 100644
index 0000000..008188d
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/DragDropSelectPointActivity.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.DragDropSelectPointScreen
+
+/**
+ * DragDropSelectPointActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 09:13
+ */
+class DragDropSelectPointActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ DragDropSelectPointScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/LogisticsActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/LogisticsActivity.kt
new file mode 100644
index 0000000..2be0966
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/LogisticsActivity.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.LogisticsScreen
+
+/**
+ * LogisticsActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 11:43
+ */
+class LogisticsActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ LogisticsScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerAnimationActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerAnimationActivity.kt
new file mode 100644
index 0000000..1b7da51
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerAnimationActivity.kt
@@ -0,0 +1,45 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.MarkerAnimationScreen
+
+/**
+ * MarkerAnimationActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 15:33
+ */
+class MarkerAnimationActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MarkerAnimationScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerClusterActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerClusterActivity.kt
new file mode 100644
index 0000000..1ed22ae
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MarkerClusterActivity.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.MarkerClusterScreen
+
+/**
+ * MarkerClusterActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 16:55
+ */
+class MarkerClusterActivity: ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MarkerClusterScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity.kt
new file mode 100644
index 0000000..ca3cef9
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.MovementTrackScreen
+
+/**
+ * MovementTrackActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:31
+ */
+class MovementTrackActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MovementTrackScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity2.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity2.kt
new file mode 100644
index 0000000..dfea126
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/MovementTrackActivity2.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.MovementTrackScreen2
+
+/**
+ * MovementTrackActivity2
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:31
+ */
+class MovementTrackActivity2 : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MovementTrackScreen2()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/RoutePlanActivity.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/RoutePlanActivity.kt
new file mode 100644
index 0000000..d9a0e17
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/RoutePlanActivity.kt
@@ -0,0 +1,44 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import com.melody.tencentmap.myapplication.ui.RoutePlanScreen
+
+/**
+ * RoutePlanActivity
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 16:28
+ */
+class RoutePlanActivity: ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ RoutePlanScreen()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/DragDropSelectPointContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/DragDropSelectPointContract.kt
new file mode 100644
index 0000000..43c2523
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/DragDropSelectPointContract.kt
@@ -0,0 +1,62 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.tencent.lbssearch.`object`.result.SearchResultObject
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+
+/**
+ * DragDropSelectPointContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 09:16
+ */
+class DragDropSelectPointContract {
+ sealed class Event : IUiEvent {
+ object ShowOpenGPSDialog : Event()
+ object HideOpenGPSDialog : Event()
+ }
+
+ data class State(
+ // 是否点击了强制定位
+ val isClickForceStartLocation: Boolean,
+ // 是否打开了系统GPS权限
+ val isOpenGps: Boolean?,
+ // 是否显示打开GPS的确认弹框
+ val isShowOpenGPSDialog: Boolean,
+ // 当前用户自身定位所在的位置
+ val currentLocation: LatLng?,
+ // 当前手持设备的方向
+ val currentRotation: Float,
+ // poi列表
+ val poiItems: List?,
+ ) : IUiState
+
+ sealed class Effect : IUiEffect {
+ internal data class Toast(val msg: String?) : Effect()
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LocationTrackingContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LocationTrackingContract.kt
index e6b6756..8dd2bbb 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LocationTrackingContract.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LocationTrackingContract.kt
@@ -52,6 +52,10 @@ class LocationTrackingContract {
val grantLocationPermission:Boolean,
// 当前位置的经纬度
val locationLatLng: LatLng?,
+ // 当前位置圆心的半径
+ val locationCircleRadius: Float,
+ // 当前手持设备的方向
+ val currentRotation: Float,
val mapProperties: MapProperties,
val mapUiSettings: MapUiSettings
) : IUiState
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LogisticsContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LogisticsContract.kt
new file mode 100644
index 0000000..ad0e8ec
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/LogisticsContract.kt
@@ -0,0 +1,61 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.map.tencent_compose.poperties.MapProperties
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.melody.tencentmap.myapplication.model.BaseRouteDataState
+import com.melody.tencentmap.myapplication.model.LogisticsRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+
+/**
+ * LogisticsContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 13:41
+ */
+class LogisticsContract {
+
+ sealed class Event : IUiEvent {
+ object QueryRoutePlan : Event()
+ }
+
+ data class State(
+ val isLoading: Boolean,
+ val fromPoint: LatLng,
+ val toPoint: LatLng,
+ val uiSettings: MapUiSettings,
+ val mapProperties: MapProperties,
+ // 目前腾讯地图的bug,有几率move过程中,首次添加polyline,出现末尾大范围断线的问题
+ val fixPolylineRainbow: Boolean,
+ val routePlanDataState: LogisticsRouteDataState?
+ ) : IUiState
+
+ sealed class Effect : IUiEffect {
+ internal class Toast(val msg: String?) : Effect()
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerAnimationContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerAnimationContract.kt
new file mode 100644
index 0000000..2d1c300
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerAnimationContract.kt
@@ -0,0 +1,54 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.map.tencent_compose.poperties.MapProperties
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.TranslateAnimation
+
+/**
+ * MarkerAnimationContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 16:29
+ */
+class MarkerAnimationContract {
+ sealed class Event : IUiEvent {
+ object StartMarkerAnimation: Event()
+ object FinishMarkerAnimation: Event()
+ }
+
+ data class State(
+ val mapUiSettings: MapUiSettings,
+ val mapLoaded: Boolean,
+ val markerDefaultLocation: LatLng,
+ val markerAnimation: TranslateAnimation?
+ ) : IUiState
+
+ sealed class Effect : IUiEffect
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerClusterContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerClusterContract.kt
new file mode 100644
index 0000000..5faaa0c
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MarkerClusterContract.kt
@@ -0,0 +1,47 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.melody.tencentmap.myapplication.model.MapClusterItem
+
+/**
+ * MarkerClusterContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 09:37
+ */
+class MarkerClusterContract {
+ sealed class Event : IUiEvent
+
+ data class State(
+ val mapUiSettings: MapUiSettings,
+ val mapClusterItems: List?
+ ) : IUiState
+
+ sealed class Effect : IUiEffect
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MovementTrackContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MovementTrackContract.kt
new file mode 100644
index 0000000..7aa8aec
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/MovementTrackContract.kt
@@ -0,0 +1,55 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * MovementTrackContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:37
+ */
+class MovementTrackContract {
+ sealed class Event : IUiEvent
+
+ data class State (
+ val isLoading: Boolean,
+ val uiSettings: MapUiSettings,
+ val latLngList: List,
+ val trackLatLng: LatLngBounds?,
+ val polylineAnimDuration: Int,
+ val polylineRainbow: PolylineRainbow?,
+ val polylineAnimation: Animation?,
+ ) : IUiState
+
+ sealed class Effect : IUiEffect
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/RoutePlanContract.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/RoutePlanContract.kt
new file mode 100644
index 0000000..160d4a6
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/contract/RoutePlanContract.kt
@@ -0,0 +1,60 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.contract
+
+import com.melody.map.tencent_compose.poperties.MapProperties
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.state.IUiEffect
+import com.melody.sample.common.state.IUiEvent
+import com.melody.sample.common.state.IUiState
+import com.melody.tencentmap.myapplication.model.BaseRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+
+/**
+ * RoutePlanContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2022/10/14 14:45
+ */
+class RoutePlanContract {
+
+ sealed class Event : IUiEvent {
+ object RoadTrafficClick: Event()
+ data class QueryRoutePlan(val queryType: Int): Event()
+ }
+
+ data class State (
+ val isLoading: Boolean,
+ val fromPoint: LatLng,
+ val toPoint: LatLng,
+ val uiSettings: MapUiSettings,
+ val mapProperties: MapProperties,
+ val routePlanDataState: BaseRouteDataState?
+ ) : IUiState
+
+ sealed class Effect : IUiEffect {
+ internal class Toast(val msg: String?): Effect()
+ }
+}
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BaseRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BaseRouteDataState.kt
new file mode 100644
index 0000000..15e2e38
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BaseRouteDataState.kt
@@ -0,0 +1,45 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * BaseRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/20 15:29
+ */
+@Stable
+open class BaseRouteDataState(
+ val startPoint: LatLng,
+ val endPoint: LatLng,
+ val polylineWidth: Float,
+ val polylineBorderWidth: Float,
+ val latLngBounds: LatLngBounds,
+ val polylineAnim: Animation?
+)
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BusRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BusRouteDataState.kt
new file mode 100644
index 0000000..2533e68
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/BusRouteDataState.kt
@@ -0,0 +1,53 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.tencent.lbssearch.`object`.result.TransitResultObject
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * BusRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/21 15:04
+ */
+@Stable
+class BusRouteDataState(
+ startPoint: LatLng,
+ endPoint: LatLng,
+ polylineWidth: Float,
+ polylineBorderWidth: Float,
+ latLngBounds: LatLngBounds,
+ val routeList: List
+): BaseRouteDataState(
+ startPoint = startPoint,
+ endPoint = endPoint,
+ polylineWidth = polylineWidth,
+ polylineBorderWidth = polylineBorderWidth,
+ polylineAnim = null,
+ latLngBounds = latLngBounds
+)
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/DrivingRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/DrivingRouteDataState.kt
new file mode 100644
index 0000000..7b68efc
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/DrivingRouteDataState.kt
@@ -0,0 +1,54 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * DrivingRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2022/10/19 09:18
+ */
+@Stable
+class DrivingRouteDataState(
+ startPoint: LatLng,
+ endPoint: LatLng,
+ polylineWidth: Float,
+ polylineBorderWidth: Float,
+ latLngBounds: LatLngBounds,
+ polylineAnim: Animation?,
+ val points: List>
+): BaseRouteDataState(
+ startPoint = startPoint,
+ endPoint = endPoint,
+ polylineWidth = polylineWidth,
+ polylineBorderWidth = polylineBorderWidth,
+ polylineAnim = polylineAnim,
+ latLngBounds = latLngBounds
+)
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/LogisticsRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/LogisticsRouteDataState.kt
new file mode 100644
index 0000000..1823150
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/LogisticsRouteDataState.kt
@@ -0,0 +1,55 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * LogisticsRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2022/10/19 09:18
+ */
+@Stable
+class LogisticsRouteDataState(
+ startPoint: LatLng,
+ endPoint: LatLng,
+ polylineWidth: Float,
+ latLngBounds: LatLngBounds,
+ val carRotation: Double,
+ val carLocation: LatLng,
+ val rainbow: PolylineRainbow,
+ val points: List
+): BaseRouteDataState(
+ startPoint = startPoint,
+ endPoint = endPoint,
+ polylineWidth = polylineWidth,
+ polylineBorderWidth = 0F,
+ polylineAnim = null,
+ latLngBounds = latLngBounds
+)
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/MapClusterItem.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/MapClusterItem.kt
new file mode 100644
index 0000000..9d052f6
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/MapClusterItem.kt
@@ -0,0 +1,37 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterItem
+
+/**
+ * MapClusterItem
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 09:40
+ */
+class MapClusterItem(private val latitude: Double, private val longitude: Double, val tag: Any? = null): ClusterItem {
+ override fun getPosition(): LatLng = LatLng(latitude, longitude)
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/RideRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/RideRouteDataState.kt
new file mode 100644
index 0000000..7bf3ed9
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/RideRouteDataState.kt
@@ -0,0 +1,54 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * BusRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/22 11:13
+ */
+@Stable
+class RideRouteDataState(
+ startPoint: LatLng,
+ endPoint: LatLng,
+ polylineWidth: Float,
+ polylineBorderWidth: Float,
+ latLngBounds: LatLngBounds,
+ polylineAnim: Animation?,
+ val ridePoints: List>
+): BaseRouteDataState(
+ startPoint = startPoint,
+ endPoint = endPoint,
+ polylineWidth = polylineWidth,
+ polylineBorderWidth = polylineBorderWidth,
+ polylineAnim = polylineAnim,
+ latLngBounds = latLngBounds
+)
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/WalkRouteDataState.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/WalkRouteDataState.kt
new file mode 100644
index 0000000..c339751
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/model/WalkRouteDataState.kt
@@ -0,0 +1,51 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.model
+
+import androidx.compose.runtime.Stable
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+
+/**
+ * BusRouteDataState
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/22 10:24
+ */
+@Stable
+class WalkRouteDataState(
+ startPoint: LatLng,
+ endPoint: LatLng,
+ polylineWidth: Float,
+ latLngBounds: LatLngBounds,
+ val wakingPoints: List>
+): BaseRouteDataState(
+ startPoint = startPoint,
+ endPoint = endPoint,
+ polylineWidth = polylineWidth,
+ polylineBorderWidth = 0F,
+ polylineAnim = null,
+ latLngBounds = latLngBounds
+)
+
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/DragDropSelectPointRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/DragDropSelectPointRepository.kt
index 87a9a14..ad8ba70 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/DragDropSelectPointRepository.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/DragDropSelectPointRepository.kt
@@ -25,6 +25,16 @@ package com.melody.tencentmap.myapplication.repo
import android.content.Context
import android.location.LocationManager
import com.melody.sample.common.utils.SDKUtils
+import com.tencent.lbssearch.TencentSearch
+import com.tencent.lbssearch.httpresponse.BaseObject
+import com.tencent.lbssearch.`object`.param.Geo2AddressParam
+import com.tencent.lbssearch.`object`.param.SearchParam
+import com.tencent.lbssearch.`object`.result.Geo2AddressResultObject
+import com.tencent.lbssearch.`object`.result.SearchResultObject
+import com.tencent.map.tools.net.http.HttpResponseListener
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import kotlinx.coroutines.suspendCancellableCoroutine
+
/**
* DragDropSelectPointRepository
@@ -35,9 +45,122 @@ import com.melody.sample.common.utils.SDKUtils
*/
object DragDropSelectPointRepository {
+ /**
+ * 勾选WebService API,点击签名校验,复制代码的话,【你要自己替换成你自己的SECRET_KEY】
+ */
+ private const val WEB_SERVICE_API_SECRET_KEY = "W79RgYY0lOIrzukvPoLM2E0DZjkKg4Cj"
+
fun checkGPSIsOpen(): Boolean {
val locationManager: LocationManager? = SDKUtils.getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
return locationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER)?: false
}
+
+ /**
+ * 查询附近1000米范围内的数据
+ */
+ suspend fun queryPoiResult(latLng: LatLng, cityName: String,onSuccess:(List)->Unit,onFailed:(String?)->Unit) {
+ val searchCityName: String
+ var searchAddress: String
+ val geo2AddressPoints = mutableListOf()
+ // 先通过逆地址编码解析
+ val geo2AddressResult = kotlin.runCatching {
+ geo2Address(latLng)
+ }
+ if (geo2AddressResult.isSuccess) {
+ // 获取搜索要传递的城市名称
+ searchCityName = cityName.ifBlank { geo2AddressResult.getOrNull()?.address_component?.city ?: "" }
+ // 获取搜索要传递的地址名称
+ searchAddress = geo2AddressResult.getOrNull()?.address ?: ""
+ val geo2AddressPoiList = geo2AddressResult.getOrNull()?.pois
+ if (geo2AddressPoiList?.isNotEmpty() == true) {
+ // 排序
+ geo2AddressPoiList.sortBy { it._distance }
+
+ searchAddress = searchAddress.ifBlank { geo2AddressPoiList[0].address ?: "" }
+
+ for (data in geo2AddressPoiList) {
+ geo2AddressPoints.add(SearchResultObject.SearchResultData().apply {
+ this.id = data.id ?: "0"
+ this.title = data.title ?: ""
+ this.address = data.address ?: ""
+ })
+ }
+ }
+ } else {
+ onFailed.invoke(geo2AddressResult.exceptionOrNull()?.message)
+ return
+ }
+ if (geo2AddressPoints.size < 2) { // 换个接口去查询试试,并扩大搜索范围
+ val searchQueryPoiResult = kotlin.runCatching {
+ doSearchQueryPoi(
+ latLng,
+ searchCityName,
+ searchAddress
+ )
+ }
+ if (searchQueryPoiResult.isSuccess) {
+ onSuccess.invoke(searchQueryPoiResult.getOrNull() ?: emptyList())
+ } else {
+ onFailed.invoke(searchQueryPoiResult.exceptionOrNull()?.message)
+ }
+ } else {
+ onSuccess.invoke(geo2AddressPoints)
+ }
+ }
+
+ /**
+ * 逆地址编码
+ */
+ private suspend fun geo2Address(latLng: LatLng): Geo2AddressResultObject.ReverseAddressResult {
+ return suspendCancellableCoroutine { continuation ->
+ val tencentSearch = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ val geo2AddressParam = Geo2AddressParam(latLng).getPoi(true)
+ .setPoiOptions(
+ Geo2AddressParam.PoiOptions()
+ .setRadius(1000)
+ .setPageSize(20)
+ .setPolicy(Geo2AddressParam.PoiOptions.POLICY_DEFAULT)
+ )
+ tencentSearch.geo2address(geo2AddressParam, object: HttpResponseListener {
+ override fun onSuccess(p0: Int, arg1: BaseObject?) {
+ val obj = arg1 as? Geo2AddressResultObject?
+ if (null == obj?.result) {
+ continuation.resumeWith(Result.failure(NullPointerException()))
+ return
+ }
+ continuation.resumeWith(Result.success(obj.result))
+
+ }
+ override fun onFailure(p0: Int, msg: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(msg)))
+ }
+ })
+ }
+ }
+
+ /**
+ * 搜索附近1000米内的地址数据
+ */
+ private suspend fun doSearchQueryPoi(latLng: LatLng,cityName:String,addressName:String):List {
+ return suspendCancellableCoroutine { continuation ->
+ val search = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ val nearBy = SearchParam.Nearby(latLng, 1000)
+ //圆形范围搜索, autoExtend(false) => 设置搜索范围不扩大,这里传true,扩大搜索范围
+ val searchParam = SearchParam(addressName, SearchParam.Region(cityName).autoExtend(true)).pageSize(20).boundary(nearBy)
+ search.search(searchParam,object: HttpResponseListener{
+ override fun onSuccess(arg0: Int, arg1: BaseObject?) {
+ val obj = arg1 as? SearchResultObject?
+ if (obj?.data == null) {
+ continuation.resumeWith(Result.success(emptyList()))
+ return
+ }
+ continuation.resumeWith(Result.success(obj.data))
+ }
+ override fun onFailure(p0: Int, msg: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(msg)))
+ }
+ })
+ }
+ }
}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LocationTrackingRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LocationTrackingRepository.kt
index 78bb370..a3b3c77 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LocationTrackingRepository.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LocationTrackingRepository.kt
@@ -22,16 +22,13 @@
package com.melody.tencentmap.myapplication.repo
-import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
-import android.graphics.Matrix
import android.location.Location
import android.os.Looper
-import com.melody.map.tencent_compose.poperties.MapProperties
import com.melody.map.tencent_compose.poperties.MapUiSettings
import com.melody.sample.common.utils.SDKUtils
-import com.melody.ui.components.R
+import com.melody.tencentmap.myapplication.R
import com.tencent.map.geolocation.TencentLocation
import com.tencent.map.geolocation.TencentLocationListener
import com.tencent.map.geolocation.TencentLocationManager
@@ -49,20 +46,22 @@ import com.tencent.tencentmap.mapsdk.maps.model.MyLocationStyle
*/
object LocationTrackingRepository {
+ /**
+ * 去除默认的蓝点
+ */
fun initMyLocationStyle(): MyLocationStyle {
- // 注意:保证地图初始化完成之后,再调用,否则getActiveMapContext是null,fromBitmap返回的也是null
- val locationIcon = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(SDKUtils.getApplicationContext().resources,R.drawable.ic_map_location_self))
+ // 使用一个1x1像素的透明图片替换默认的蓝点图标
+ val locationIcon = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(SDKUtils.getApplicationContext().resources,
+ R.drawable.ic_transparent_location))
return MyLocationStyle().apply {
// 设置小蓝点的图标
icon(locationIcon)
// 设置定位的类型为定位模式 ,可以由定位、跟随或地图根据面向方向旋转几种
- myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE)
+ myLocationType(MyLocationStyle.LOCATION_TYPE_MAP_ROTATE_NO_CENTER) // 此种类型:不会移动到地图中心点
// 设置圆形的边框颜色
- strokeColor(Color.argb(100, 0, 0, 180))
+ strokeColor(Color.TRANSPARENT)
// 设置圆形的填充颜色
- fillColor(Color.argb(100, 0, 0, 180))
- // 设置圆形的边框粗细
- strokeWidth(1)
+ fillColor(Color.TRANSPARENT)
}
}
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LogisticsRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LogisticsRepository.kt
new file mode 100644
index 0000000..70e8116
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/LogisticsRepository.kt
@@ -0,0 +1,146 @@
+package com.melody.tencentmap.myapplication.repo
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import com.melody.map.tencent_compose.model.MapType
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.melody.map.tencent_compose.poperties.MapProperties
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.utils.SDKUtils
+import com.melody.tencentmap.myapplication.model.LogisticsRouteDataState
+import com.tencent.lbssearch.TencentSearch
+import com.tencent.lbssearch.httpresponse.HttpResponseListener
+import com.tencent.lbssearch.`object`.param.DrivingParam
+import com.tencent.lbssearch.`object`.result.DrivingResultObject
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * LogisticsRepository
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 13:40
+ */
+object LogisticsRepository {
+ /**
+ * 勾选WebService API,点击签名校验,复制代码的话,【你要自己替换成你自己的SECRET_KEY】
+ */
+ private const val WEB_SERVICE_API_SECRET_KEY = "W79RgYY0lOIrzukvPoLM2E0DZjkKg4Cj"
+
+ fun initMapUiSettings() : MapUiSettings {
+ return MapUiSettings(
+ isScrollGesturesEnabled = true,
+ isZoomGesturesEnabled = true,
+ isScaleControlsEnabled = true
+ )
+ }
+
+ fun initMapProperties() : MapProperties {
+ return MapProperties(mapType = MapType.NORMAL, enableMultipleInfoWindow = true)
+ }
+
+ private fun convertLatLngBounds(allPolyLines: List): LatLngBounds {
+ val b: LatLngBounds.Builder = LatLngBounds.builder()
+ for (point in allPolyLines) {
+ b.include(point)
+ }
+ return b.build()
+ }
+
+ suspend fun getRoutePlan(fromPoint:LatLng, toPoint:LatLng): LogisticsRouteDataState {
+ return suspendCancellableCoroutine { continuation ->
+ val drivingParam = DrivingParam(fromPoint, toPoint) //创建导航参数
+ drivingParam.roadType(DrivingParam.RoadType.ON_MAIN_ROAD)
+ drivingParam.heading(90)
+ drivingParam.accuracy(30)
+ val tencentSearch = TencentSearch(
+ SDKUtils.getApplicationContext(),
+ WEB_SERVICE_API_SECRET_KEY
+ )
+ tencentSearch.getRoutePlan(drivingParam, object : HttpResponseListener {
+ override fun onSuccess(p0: Int, p1 : DrivingResultObject?) {
+ if(p1?.result == null) {
+ continuation.resumeWith(Result.failure(NullPointerException("路线获取失败,reason:DrivingResultObject = null")))
+ return
+ }
+ val points= (p1.result.routes?.map { it.polyline }?: emptyList())
+ if(points.isEmpty()) {
+ continuation.resumeWith(Result.failure(NullPointerException("路线结果为空")))
+ return
+ }
+ // 这里模拟运送到3/4的位置,模拟模拟,请根据你自己的实际业务自己处理
+ // 这是模拟,这是模拟,这是模拟,这是模拟,这是模拟,这是模拟,这是模拟,这是模拟,这是模拟
+ val carLocation = points[0][points[0].size*3/4]
+ val carRotation = calcCarMoveRotation(carLocation, fromPoint)
+ continuation.resumeWith(Result.success(
+ LogisticsRouteDataState(
+ polylineWidth = 10F,
+ startPoint = fromPoint,
+ endPoint = toPoint,
+ carRotation = carRotation,
+ carLocation = carLocation,
+ // 这里模拟运送到3/4的位置了,实际应该根据当前位置和收货地址进行计算边界,模拟拼多多
+ latLngBounds = convertLatLngBounds(points[0].subList(points[0].size*3/4,points[0].size)),
+ rainbow = initPolylineRainbow(points[0].size),
+ points = points[0]
+ )
+ ))
+ }
+
+ override fun onFailure(p0: Int, p1: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(p1)))
+ }
+ })
+ }
+ }
+
+ /**
+ * 计算物流的小车当前移动的方向,用于设置给Marker
+ */
+ private fun calcCarMoveRotation(
+ locationLatLng: LatLng,
+ fromPoint: LatLng
+ ): Double {
+ val slope =
+ ((locationLatLng.latitude - fromPoint.latitude) / (locationLatLng.longitude - fromPoint.longitude))
+ val radio: Double = Math.atan(slope)
+ var angle: Double = 180 * (radio / Math.PI)
+ angle = if (slope > 0) {
+ if (locationLatLng.longitude < fromPoint.longitude) {
+ -90 - angle
+ } else {
+ 90 - angle
+ }
+ } else if (slope == 0.0) {
+ if (locationLatLng.longitude < fromPoint.longitude) {
+ -90.0
+ } else {
+ 90.0
+ }
+ } else {
+ if (locationLatLng.longitude < locationLatLng.latitude) {
+ 90 - angle
+ } else {
+ -90 - angle
+ }
+ }
+ return angle
+ }
+
+ /**
+ * 彩虹线段配置
+ */
+ fun initPolylineRainbow(totalSize:Int): PolylineRainbow {
+ return PolylineRainbow.create(
+ colors = listOf(
+ Color(0xFF58C180).toArgb(),
+ Color(0xFF99ECB9).toArgb(),
+ Color(0xFF99ECB9).toArgb()
+ ),
+ indexes = listOf(0,totalSize*3/4,totalSize)
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MainRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MainRepository.kt
index 0f2f670..17ea980 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MainRepository.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MainRepository.kt
@@ -26,9 +26,16 @@ import android.content.Intent
import com.melody.sample.common.utils.SDKUtils
import com.melody.sample.common.utils.StringUtils
import com.melody.tencentmap.myapplication.BasicFeatureActivity
+import com.melody.tencentmap.myapplication.DragDropSelectPointActivity
import com.melody.tencentmap.myapplication.LocationTrackingActivity
+import com.melody.tencentmap.myapplication.LogisticsActivity
+import com.melody.tencentmap.myapplication.MarkerAnimationActivity
+import com.melody.tencentmap.myapplication.MarkerClusterActivity
+import com.melody.tencentmap.myapplication.MovementTrackActivity
+import com.melody.tencentmap.myapplication.MovementTrackActivity2
import com.melody.tencentmap.myapplication.OverlayActivity
import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.RoutePlanActivity
import com.melody.tencentmap.myapplication.SmoothMoveActivity
/**
@@ -53,24 +60,29 @@ object MainRepository {
StringUtils.getString(R.string.tx_map_main_feature_item_smooth_move) -> {
startActivity(Intent(SDKUtils.getApplicationContext(), SmoothMoveActivity::class.java))
}
- /*StringUtils.getString(R.string.tx_map_main_feature_item_drag_drop_select_point) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),DragDropSelectPointActivity::class.java))
+ StringUtils.getString(R.string.tx_map_main_feature_item_drag_drop_select_point) -> {
+ startActivity(Intent(SDKUtils.getApplicationContext(), DragDropSelectPointActivity::class.java))
}
StringUtils.getString(R.string.tx_map_main_feature_item_route_plan) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),RoutePlanActivity::class.java))
+ startActivity(Intent(SDKUtils.getApplicationContext(), RoutePlanActivity::class.java))
}
- StringUtils.getString(R.string.tx_map_main_feature_item_multipoint_click) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),MultiPointOverlayActivity::class.java))
+ StringUtils.getString(R.string.tx_map_main_feature_item_logistics) -> {
+ startActivity(Intent(SDKUtils.getApplicationContext(), LogisticsActivity::class.java))
}
StringUtils.getString(R.string.tx_map_main_feature_item_marker_animation) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),MarkerAnimationActivity::class.java))
+ startActivity(Intent(SDKUtils.getApplicationContext(), MarkerAnimationActivity::class.java))
}
StringUtils.getString(R.string.tx_map_main_feature_item_movement_track) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),MovementTrackActivity::class.java))
+ startActivity(Intent(SDKUtils.getApplicationContext(), MovementTrackActivity::class.java))
+ }
+ StringUtils.getString(R.string.tx_map_main_feature_item_movement_track2) -> {
+ // 其实 OverlayActivity 这里面已经有移动的示例了,再拉一个页面写,防止有些同学又搞晕了
+ // 这里用了腾讯地图的线段动画的能力,包括后面的【路径规划示例】,不会很死板,我们加了线段动画
+ startActivity(Intent(SDKUtils.getApplicationContext(), MovementTrackActivity2::class.java))
}
StringUtils.getString(R.string.tx_map_main_feature_item_cluster_effect) -> {
- startActivity(Intent(SDKUtils.getApplicationContext(),ClusterEffectActivity::class.java))
- }*/
+ startActivity(Intent(SDKUtils.getApplicationContext(), MarkerClusterActivity::class.java))
+ }
}
}
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerAnimationRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerAnimationRepository.kt
new file mode 100644
index 0000000..731e52a
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerAnimationRepository.kt
@@ -0,0 +1,30 @@
+package com.melody.tencentmap.myapplication.repo
+
+import android.view.animation.AccelerateDecelerateInterpolator
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.TranslateAnimation
+
+/**
+ * MarkerAnimationRepository
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 16:32
+ */
+object MarkerAnimationRepository {
+
+ fun initMapUiSettings(): MapUiSettings {
+ return MapUiSettings(
+ isZoomGesturesEnabled = true,
+ isScrollGesturesEnabled = true,
+ )
+ }
+
+ fun prepareMarkerAnimation(latLng: LatLng):TranslateAnimation {
+ return TranslateAnimation(latLng).apply {
+ duration = 2500
+ interpolator = AccelerateDecelerateInterpolator()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerClusterRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerClusterRepository.kt
new file mode 100644
index 0000000..2d1f155
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MarkerClusterRepository.kt
@@ -0,0 +1,38 @@
+package com.melody.tencentmap.myapplication.repo
+
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.tencentmap.myapplication.model.MapClusterItem
+
+/**
+ * MarkerClusterRepository
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 09:38
+ */
+object MarkerClusterRepository {
+
+ fun initMapUiSettings(): MapUiSettings {
+ return MapUiSettings(
+ isZoomGesturesEnabled = true,
+ isScrollGesturesEnabled = true,
+ )
+ }
+
+ fun initClusterItems(): ArrayList {
+ val items: ArrayList = ArrayList()
+ items.add(MapClusterItem(39.984059, 116.307621, "可以自己定义对象,我自己控制里面的数据"))
+ items.add(MapClusterItem(39.981954, 116.304703, "啊?"))
+ items.add(MapClusterItem(39.984355, 116.312256, "哦?"))
+ items.add(MapClusterItem(39.980442, 116.315346, "额?"))
+ items.add(MapClusterItem(39.981527, 116.308994, "哈?"))
+ items.add(MapClusterItem(39.979751, 116.310539, "嗯?"))
+ items.add(MapClusterItem(39.977252, 116.305776, "切?"))
+ items.add(MapClusterItem(39.984026, 116.316419, "呵?"))
+ items.add(MapClusterItem(39.976956, 116.314874, "噗?"))
+ items.add(MapClusterItem(39.978501, 116.311827, "嘻?"))
+ items.add(MapClusterItem(39.980277, 116.312814, "噶?"))
+ items.add(MapClusterItem(39.980236, 116.369022, this))
+ return items
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MovementTrackRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MovementTrackRepository.kt
new file mode 100644
index 0000000..63ffe98
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/MovementTrackRepository.kt
@@ -0,0 +1,117 @@
+package com.melody.tencentmap.myapplication.repo
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.map.tencent_compose.utils.PathSmoothTool
+import com.melody.sample.common.utils.SDKUtils
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.EmergeAnimation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStream
+import java.io.InputStreamReader
+
+/**
+ * MovementTrackRepository
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:38
+ */
+object MovementTrackRepository {
+
+ fun initMapUiSettings(): MapUiSettings {
+ return MapUiSettings(
+ isScaleControlsEnabled = true,
+ isZoomGesturesEnabled = true,
+ isScrollGesturesEnabled = true
+ )
+ }
+
+ fun parseLocationsData(filePath: String): List {
+ val locLists: MutableList = mutableListOf()
+ var input: InputStream? = null
+ var inputReader: InputStreamReader? = null
+ var bufReader: BufferedReader? = null
+ try {
+ input = SDKUtils.getApplicationContext().assets.open(filePath)
+ inputReader = InputStreamReader(input)
+ bufReader = BufferedReader(inputReader)
+ var line:String?
+ while (bufReader.readLine().also { line = it } != null) {
+ val strArray: Array? = line?.split(",".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
+ if (null != strArray) {
+ val newpoint = LatLng(strArray[0].toDouble(), strArray[1].toDouble())
+ if (locLists.size == 0 || newpoint.toString() !== locLists[locLists.size - 1].toString()) {
+ locLists.add(newpoint)
+ }
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ } finally {
+ try {
+ if (bufReader != null) {
+ bufReader.close()
+ bufReader = null
+ }
+ if (inputReader != null) {
+ inputReader.close()
+ inputReader = null
+ }
+ if (input != null) {
+ input.close()
+ input = null
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ // 轨迹平滑处理
+ val pathSmoothTool = PathSmoothTool()
+ // 设置平滑处理的等级
+ pathSmoothTool.intensity = 4
+ return pathSmoothTool.pathOptimize(locLists)
+ }
+
+ fun getTrackLatLngBounds(pointList: List): LatLngBounds {
+ val b = LatLngBounds.builder()
+ if (pointList.isEmpty()) {
+ return b.build()
+ }
+ for (i in pointList.indices) {
+ b.include(pointList[i])
+ }
+ return b.build()
+ }
+
+ /**
+ * 彩虹线段配置
+ */
+ fun initPolylineRainbow(totalSize:Int): PolylineRainbow {
+ return PolylineRainbow.create(
+ colors = listOf(
+ Color(0xFF9FD555).toArgb(),
+ Color(0xFFD247EB).toArgb(),
+ Color(0xFF41DD5B).toArgb(),
+ Color(0xFFF38D0F).toArgb()
+ ),
+ // 腾讯地图内部会根据取完颜色值,填充到对应的index位置处
+ indexes = listOf(0,totalSize/8,totalSize/3,totalSize)
+ )
+ }
+
+ /**
+ * 线段动画
+ */
+ fun initPolylineAnimation(startLatLng: LatLng,totalDuration:Int): Animation {
+ return EmergeAnimation(startLatLng).apply {
+ duration = totalDuration.toLong()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/OverlayRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/OverlayRepository.kt
index 23ae9fc..a35f392 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/OverlayRepository.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/OverlayRepository.kt
@@ -19,8 +19,7 @@ object OverlayRepository {
fun initMapUiSetting(): MapUiSettings {
return MapUiSettings(
isZoomGesturesEnabled = true,
- isScrollGesturesEnabled = true,
- isZoomEnabled = true
+ isScrollGesturesEnabled = true
)
}
fun initPolygonPointList(): List{
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/RoutePlanRepository.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/RoutePlanRepository.kt
new file mode 100644
index 0000000..0422f06
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/repo/RoutePlanRepository.kt
@@ -0,0 +1,260 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.repo
+
+import com.melody.map.tencent_compose.model.MapType
+import com.melody.map.tencent_compose.poperties.MapProperties
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.sample.common.utils.SDKUtils
+import com.melody.tencentmap.myapplication.model.BaseRouteDataState
+import com.melody.tencentmap.myapplication.model.BusRouteDataState
+import com.melody.tencentmap.myapplication.model.DrivingRouteDataState
+import com.melody.tencentmap.myapplication.model.RideRouteDataState
+import com.melody.tencentmap.myapplication.model.WalkRouteDataState
+import com.tencent.lbssearch.TencentSearch
+import com.tencent.lbssearch.httpresponse.HttpResponseListener
+import com.tencent.lbssearch.`object`.param.BicyclingParam
+import com.tencent.lbssearch.`object`.param.DrivingParam
+import com.tencent.lbssearch.`object`.param.TransitParam
+import com.tencent.lbssearch.`object`.param.WalkingParam
+import com.tencent.lbssearch.`object`.result.BicyclingResultObject
+import com.tencent.lbssearch.`object`.result.DrivingResultObject
+import com.tencent.lbssearch.`object`.result.TransitResultObject
+import com.tencent.lbssearch.`object`.result.WalkingResultObject
+import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.EmergeAnimation
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+
+/**
+ * RoutePlanRepository
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 16:45
+ */
+object RoutePlanRepository {
+
+ /**
+ * 勾选WebService API,点击签名校验,复制代码的话,【你要自己替换成你自己的SECRET_KEY】
+ */
+ private const val WEB_SERVICE_API_SECRET_KEY = "W79RgYY0lOIrzukvPoLM2E0DZjkKg4Cj"
+
+ fun initMapUiSettings() : MapUiSettings {
+ return MapUiSettings(
+ isScrollGesturesEnabled = true,
+ isZoomGesturesEnabled = true,
+ isScaleControlsEnabled = true
+ )
+ }
+
+ fun initMapProperties() : MapProperties {
+ return MapProperties(mapType = MapType.NORMAL, isTrafficEnabled = false)
+ }
+
+ /**
+ * 路径规划的线段动画
+ */
+ private fun initPolylineAnimation(startLatLng: LatLng, totalDuration: Int): Animation {
+ return EmergeAnimation(startLatLng).apply {
+ duration = totalDuration.toLong()
+ }
+ }
+
+ private fun convertLatLngBounds(allPolyLines: List): LatLngBounds {
+ val b: LatLngBounds.Builder = LatLngBounds.builder()
+ for (point in allPolyLines) {
+ b.include(point)
+ }
+ return b.build()
+ }
+
+ /**
+ * 查询路径规划
+ */
+ suspend fun queryRoutePlan(queryType: Int, fromPoint:LatLng, toPoint:LatLng): BaseRouteDataState {
+ return when(queryType) {
+ 0 -> drivingRoutePlanSearch(fromPoint, toPoint)
+ 1 -> busRoutePlanSearch(fromPoint, toPoint)
+ 2 -> walkRoutePlanSearch(fromPoint, toPoint)
+ else -> rideRoutePlanSearch(fromPoint, toPoint)
+ }
+ }
+
+ /**
+ * 驾车路径规划搜索
+ */
+ private suspend fun drivingRoutePlanSearch(fromPoint:LatLng, toPoint:LatLng): DrivingRouteDataState {
+ return suspendCancellableCoroutine { continuation ->
+ val drivingParam = DrivingParam(fromPoint, toPoint) //创建导航参数
+ drivingParam.roadType(DrivingParam.RoadType.ON_MAIN_ROAD_BELOW_BRIDGE)
+ drivingParam.heading(90)
+ drivingParam.accuracy(30)
+ val tencentSearch = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ tencentSearch.getRoutePlan(drivingParam, object : HttpResponseListener {
+ override fun onSuccess(p0: Int, p1 : DrivingResultObject?) {
+ if(p1?.result == null) {
+ continuation.resumeWith(Result.failure(NullPointerException()))
+ return
+ }
+ // 返回多路径
+ val points= (p1.result.routes?.map { it.polyline }?: emptyList())
+
+ continuation.resumeWith(Result.success(
+ DrivingRouteDataState(
+ polylineWidth = 24F,
+ polylineBorderWidth = 6F,
+ startPoint = fromPoint,
+ endPoint = toPoint,
+ latLngBounds = convertLatLngBounds(points[0]),
+ polylineAnim = initPolylineAnimation(fromPoint,1000),
+ points = points
+ )
+ ))
+ }
+
+ override fun onFailure(p0: Int, p1: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(p1)))
+ }
+ })
+ }
+ }
+
+ /**
+ * 公交车路径规划搜索
+ */
+ private suspend fun busRoutePlanSearch(fromPoint:LatLng, toPoint:LatLng): BusRouteDataState {
+ return suspendCancellableCoroutine { continuation ->
+ val transitParam = TransitParam(fromPoint, toPoint)
+ val tencentSearch = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ transitParam.policy(TransitParam.Policy.LEAST_WALKING, TransitParam.Preference.NO_SUBWAY)
+ tencentSearch.getRoutePlan(transitParam,object : HttpResponseListener {
+ override fun onSuccess(p0: Int, p1: TransitResultObject?) {
+ if(p1?.result == null) {
+ continuation.resumeWith(Result.failure(NullPointerException()))
+ return
+ }
+ if (p1.result != null && p1.result.routes != null && p1.result.routes.size > 0) {
+ continuation.resumeWith(Result.success(BusRouteDataState(
+ polylineWidth = 24F,
+ polylineBorderWidth = 6F,
+ startPoint = fromPoint,
+ endPoint = toPoint,
+ latLngBounds = LatLngBounds(p1.result.routes[0].bounds.northeast,p1.result.routes[0].bounds.southwest),
+ routeList = p1.result.routes
+ )))
+ } else {
+ continuation.resumeWith(Result.failure(NullPointerException("路线结果为空")))
+ }
+ }
+ override fun onFailure(p0: Int, p1: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(p1)))
+ }
+ })
+ }
+ }
+
+ /**
+ * 步行路径规划搜索
+ */
+ private suspend fun walkRoutePlanSearch(fromPoint:LatLng, toPoint:LatLng): WalkRouteDataState{
+ return suspendCancellableCoroutine { continuation ->
+ val walkingParam = WalkingParam()
+ walkingParam.from(fromPoint)
+ walkingParam.to(toPoint)
+ val tencentSearch = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ tencentSearch.getRoutePlan(walkingParam, object : HttpResponseListener {
+ override fun onSuccess(p0: Int, p1: WalkingResultObject?) {
+ if (p1 == null) {
+ continuation.resumeWith(Result.failure(NullPointerException()))
+ return
+ }
+ if (p1.result != null && p1.result.routes != null && p1.result.routes.size > 0) {
+ val walkPointList = p1.result.routes.map { it.polyline }
+ continuation.resumeWith(Result.success(
+ WalkRouteDataState(
+ startPoint = fromPoint,
+ endPoint = toPoint,
+ polylineWidth = 10F,
+ latLngBounds = convertLatLngBounds(walkPointList[0]),
+ wakingPoints = walkPointList
+ )
+ ))
+ } else {
+ continuation.resumeWith(Result.failure(NullPointerException("路线结果为空")))
+ }
+ }
+
+ override fun onFailure(
+ statusCode: Int,
+ responseString: String?,
+ throwable: Throwable?
+ ) {
+ continuation.resumeWith(Result.failure(Throwable(responseString?:throwable?.message)))
+ }
+ })
+ }
+ }
+
+ /**
+ * 骑行路径规划搜索
+ */
+ private suspend fun rideRoutePlanSearch(fromPoint:LatLng, toPoint:LatLng): RideRouteDataState {
+ return suspendCancellableCoroutine { continuation ->
+ val bicyclingParam = BicyclingParam()
+ bicyclingParam.from(fromPoint)
+ bicyclingParam.to(toPoint)
+ val tencentSearch = TencentSearch(SDKUtils.getApplicationContext(),WEB_SERVICE_API_SECRET_KEY)
+ tencentSearch.getRoutePlan(bicyclingParam, object : HttpResponseListener{
+ override fun onSuccess(p0: Int, p1: BicyclingResultObject?) {
+ if (p1 == null) {
+ continuation.resumeWith(Result.failure(NullPointerException()))
+ return
+ }
+ if (p1.result != null && p1.result.routes != null && p1.result.routes.size > 0) {
+ val ridePointList = p1.result.routes.map { it.polyline }
+ continuation.resumeWith(Result.success(
+ RideRouteDataState(
+ startPoint = fromPoint,
+ endPoint = toPoint,
+ polylineWidth = 24F,
+ polylineBorderWidth = 6F,
+ polylineAnim = initPolylineAnimation(fromPoint, 500),
+ latLngBounds = convertLatLngBounds(ridePointList[0]),
+ ridePoints = ridePointList
+ )
+ ))
+ } else {
+ continuation.resumeWith(Result.failure(NullPointerException("路线结果为空")))
+ }
+ }
+
+ override fun onFailure(p0: Int, responseString: String?, p2: Throwable?) {
+ continuation.resumeWith(Result.failure(Throwable(responseString)))
+ }
+ })
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/BasicFeatureScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/BasicFeatureScreen.kt
index aa4fc04..738cd5c 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/BasicFeatureScreen.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/BasicFeatureScreen.kt
@@ -40,8 +40,10 @@ import com.melody.map.tencent_compose.TXMap
import com.melody.map.tencent_compose.model.MapType
import com.melody.map.tencent_compose.poperties.MapProperties
import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
import com.melody.sample.common.model.ImmutableListWrapper
import com.melody.ui.components.BasicFeatureMenuBar
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
import com.tencent.tencentmap.mapsdk.maps.model.LatLng
import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
@@ -54,8 +56,8 @@ import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds
*/
@Composable
internal fun BasicFeatureScreen() {
+ val cameraPositionState = rememberCameraPositionState()
var uiSettings by remember { mutableStateOf(MapUiSettings()) }
-
var mapProperties by remember { mutableStateOf(MapProperties()) }
val menuList by remember {
@@ -65,15 +67,16 @@ internal fun BasicFeatureScreen() {
"卫星图",
"暗色地图",
"3D楼块效果",
+ "地图标注及名称",
"实时交通状况开关",
"显示室内地图开关",
+ "显示手绘图",
"设置地图显示范围",
"地图Logo缩放",
"旋转手势开关",
"拖拽手势开关",
"倾斜手势开关",
"缩放手势开关",
- "缩放按钮开关",
"指南针控件开关",
"比例尺控件开关",
"比例尺淡入淡出"
@@ -85,6 +88,7 @@ internal fun BasicFeatureScreen() {
// 地图
TXMap(
modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
uiSettings = uiSettings,
properties = mapProperties
)
@@ -105,15 +109,16 @@ internal fun BasicFeatureScreen() {
"卫星图"-> mapProperties.mapType == MapType.SATELLITE
"暗色地图"-> mapProperties.mapType == MapType.DARK
"3D楼块效果"-> mapProperties.isShowBuildings
+ "地图标注及名称"-> mapProperties.isShowMapLabels
"实时交通状况开关" -> mapProperties.isTrafficEnabled
"显示室内地图开关" -> mapProperties.isIndoorEnabled
+ "显示手绘图" -> mapProperties.isHandDrawMapEnable
"设置地图显示范围" -> if(mapProperties.mapShowLatLngBounds == null) null else "腾讯总部大楼"
"地图Logo缩放" -> uiSettings.logoScale
"旋转手势开关" -> uiSettings.isRotateGesturesEnabled
"拖拽手势开关" -> uiSettings.isScrollGesturesEnabled
"倾斜手势开关" -> uiSettings.isTiltGesturesEnabled
"缩放手势开关" -> uiSettings.isZoomGesturesEnabled
- "缩放按钮开关" -> uiSettings.isZoomEnabled
"指南针控件开关" -> uiSettings.isCompassEnabled
"比例尺控件开关" -> uiSettings.isScaleControlsEnabled
"比例尺淡入淡出" -> uiSettings.isScaleViewFadeEnable
@@ -143,12 +148,23 @@ internal fun BasicFeatureScreen() {
"3D楼块效果" -> {
mapProperties = mapProperties.copy(isShowBuildings = !mapProperties.isShowBuildings)
}
+ "地图标注及名称" -> {
+ mapProperties = mapProperties.copy(isShowMapLabels = !mapProperties.isShowMapLabels)
+ }
"实时交通状况开关"-> {
mapProperties = mapProperties.copy(isTrafficEnabled = !mapProperties.isTrafficEnabled)
}
"显示室内地图开关"-> {
mapProperties = mapProperties.copy(isIndoorEnabled = !mapProperties.isIndoorEnabled)
}
+ "显示手绘图"-> {
+ // 注意:手绘图主要用于:景区!!!!!
+ mapProperties = mapProperties.copy(isHandDrawMapEnable = !mapProperties.isHandDrawMapEnable)
+ if(mapProperties.isHandDrawMapEnable) {
+ // 地图视野移动,指定了经纬度和缩放级别
+ cameraPositionState.move(CameraUpdateFactory.newLatLngZoom(LatLng(25.072295,102.761478), 18F))
+ }
+ }
"设置地图显示范围"-> {
if(mapProperties.mapShowLatLngBounds == null) {
// 设置地图只显示腾讯总部大楼这个区域的地图范围
@@ -178,9 +194,6 @@ internal fun BasicFeatureScreen() {
"缩放手势开关"-> {
uiSettings = uiSettings.copy(isZoomGesturesEnabled = !uiSettings.isZoomGesturesEnabled)
}
- "缩放按钮开关"-> {
- uiSettings = uiSettings.copy(isZoomEnabled = !uiSettings.isZoomEnabled)
- }
"指南针控件开关"-> {
uiSettings = uiSettings.copy(isCompassEnabled = !uiSettings.isCompassEnabled)
}
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropPoiResultList.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropPoiResultList.kt
new file mode 100644
index 0000000..90527e1
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropPoiResultList.kt
@@ -0,0 +1,77 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.melody.ui.components.EmptyResultText
+import com.melody.ui.components.MapPoiItem
+import com.tencent.lbssearch.`object`.result.SearchResultObject
+
+@Composable
+internal fun DragDropPoiResultList(
+ poiItemList: List?,
+ onItemClick: (SearchResultObject.SearchResultData) -> Unit
+) {
+ val currentOnItemClick by rememberUpdatedState(newValue = onItemClick)
+ Box(modifier = Modifier.fillMaxSize().background(Color(0XFFFAFAFC))) {
+ poiItemList?.let { list ->
+ if (list.isEmpty()) {
+ EmptyResultText(
+ modifier = Modifier
+ .padding(15.dp)
+ .align(Alignment.Center),
+ text = "没有搜索到结果"
+ )
+ } else {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(15.dp)
+ ) {
+ items(items = list, key = { it.id }) {
+ MapPoiItem(
+ title = it.title,
+ addressName = it.address,
+ cityName = null,
+ snippet = null
+ ) {
+ currentOnItemClick.invoke(it)
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropSelectPointScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropSelectPointScreen.kt
new file mode 100644
index 0000000..94f130b
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/DragDropSelectPointScreen.kt
@@ -0,0 +1,176 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import android.Manifest
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.google.accompanist.permissions.ExperimentalPermissionsApi
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.map.tencent_compose.poperties.MapUiSettings
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.sample.common.launcher.handlerGPSLauncher
+import com.melody.sample.common.utils.requestMultiplePermission
+import com.melody.sample.common.utils.showToast
+import com.melody.tencentmap.myapplication.contract.DragDropSelectPointContract
+import com.melody.tencentmap.myapplication.dialog.ShowOpenGPSDialog
+import com.melody.tencentmap.myapplication.viewmodel.DragDropSelectPointViewModel
+import com.melody.ui.components.ForceStartLocationButton
+import com.melody.ui.components.UIMarkerInScreenCenter
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * DragDropSelectPointContract
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 09:14
+ */
+@OptIn(ExperimentalPermissionsApi::class)
+@Composable
+internal fun DragDropSelectPointScreen() {
+ var isMapLoaded by rememberSaveable{ mutableStateOf(false) }
+ val dragDropAnimatable = remember { Animatable(Size.Zero,Size.VectorConverter) }
+ val cameraPositionState = rememberCameraPositionState()
+ val locationState = rememberMarkerState()
+ val viewModel: DragDropSelectPointViewModel = viewModel()
+ val currentState by viewModel.uiState.collectAsState()
+
+ val openGpsLauncher = handlerGPSLauncher(viewModel::checkGpsStatus)
+
+ val reqGPSPermission = requestMultiplePermission(
+ permissions = listOf(
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ ),
+ onNoGrantPermission = viewModel::handleNoGrantLocationPermission,
+ onGrantAllPermission = viewModel::checkGpsStatus
+ )
+
+ LaunchedEffect(viewModel.effect) {
+ viewModel.effect.onEach {
+ if(it is DragDropSelectPointContract.Effect.Toast) {
+ showToast(it.msg)
+ }
+ }.collect()
+ }
+
+ LaunchedEffect(Unit) {
+ snapshotFlow { reqGPSPermission.allPermissionsGranted }.collect {
+ viewModel.checkGpsStatus()
+ }
+ }
+
+ LaunchedEffect(currentState.isOpenGps, reqGPSPermission.allPermissionsGranted) {
+ if(currentState.isOpenGps == true) {
+ if (!reqGPSPermission.allPermissionsGranted) {
+ reqGPSPermission.launchMultiplePermissionRequest()
+ } else {
+ viewModel.startMapLocation()
+ }
+ }
+ }
+
+ // 地图移动,中心的Marker需要动画跳动
+ LaunchedEffect(cameraPositionState.isMoving) {
+ if (cameraPositionState.isMoving) {
+ dragDropAnimatable.animateTo(Size(45F,20F))
+ } else {
+ dragDropAnimatable.animateTo(Size(25F,11F))
+ // 查询附近1000米地址数据
+ viewModel.doSearchQueryPoi(cameraPositionState.position.latlng,"")
+ }
+ }
+
+ LaunchedEffect(currentState.isClickForceStartLocation, currentState.currentLocation) {
+ val curLocation = currentState.currentLocation
+ if(null == curLocation || cameraPositionState.position.latlng == curLocation) return@LaunchedEffect
+ locationState.position = curLocation
+ cameraPositionState.animate(CameraUpdateFactory.newLatLngZoom(currentState.currentLocation, 17F))
+ }
+
+ if(currentState.isShowOpenGPSDialog) {
+ ShowOpenGPSDialog(
+ onDismiss = viewModel::hideOpenGPSDialog,
+ onPositiveClick = {
+ viewModel.openGPSPermission(openGpsLauncher)
+ }
+ )
+ }
+
+ Column(modifier = Modifier.fillMaxSize()) {
+ Box(modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight(0.5F)) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ uiSettings = MapUiSettings(
+ isZoomGesturesEnabled = true,
+ isScrollGesturesEnabled = true,
+ ),
+ onMapLoaded = {
+ isMapLoaded = true
+ }
+ ){
+ Marker(
+ state = locationState,
+ anchor = Offset(0.5F,0.5F),
+ rotation = currentState.currentRotation,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_location_self),
+ onClick = { true }
+ )
+ }
+ if(isMapLoaded) {
+ // 地图加载出来之后,再显示出来选点的图标
+ UIMarkerInScreenCenter(com.melody.tencentmap.myapplication.R.drawable.purple_pin) {
+ dragDropAnimatable.value
+ }
+ }
+ // 强制触发单次定位的按钮
+ ForceStartLocationButton(viewModel::startMapLocation)
+ }
+
+ //选点获取到的结果
+ DragDropPoiResultList(
+ poiItemList = currentState.poiItems,
+ onItemClick = viewModel::showSelectAddressInfo
+ )
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LocationTrackingScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LocationTrackingScreen.kt
index 4b321f9..15f41cc 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LocationTrackingScreen.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LocationTrackingScreen.kt
@@ -23,22 +23,29 @@
package com.melody.tencentmap.myapplication.ui
import android.Manifest
+import androidx.compose.runtime.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.melody.map.tencent_compose.TXMap
-import com.melody.map.tencent_compose.model.TXCameraPosition
+import com.melody.map.tencent_compose.overlay.Circle
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
import com.melody.map.tencent_compose.position.rememberCameraPositionState
import com.melody.sample.common.launcher.handlerGPSLauncher
import com.melody.sample.common.utils.requestMultiplePermission
import com.melody.sample.common.utils.showToast
+import com.melody.ui.components.R
import com.melody.tencentmap.myapplication.contract.LocationTrackingContract
import com.melody.tencentmap.myapplication.dialog.ShowOpenGPSDialog
import com.melody.tencentmap.myapplication.viewmodel.LocationTrackingViewModel
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
import com.tencent.tencentmap.mapsdk.maps.model.LatLng
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
@@ -55,10 +62,11 @@ import kotlinx.coroutines.flow.onEach
internal fun LocationTrackingScreen() {
val viewModel: LocationTrackingViewModel = viewModel()
val currentState by viewModel.uiState.collectAsState()
- val cameraPosition = rememberCameraPositionState {
- // 不预加载显示默认北京的位置,注意:如果这里想用onMapLoaded回调,则需要【指定TXCameraPosition的位置坐标】
- position = TXCameraPosition(LatLng(0.0, 0.0), 11f, 0f, 0f)
- }
+ var isRenderLocation by rememberSaveable{ mutableStateOf(false) }
+ val cameraPosition = rememberCameraPositionState()
+
+ val locationIconState = rememberMarkerState(position = currentState.locationLatLng?: LatLng(0.0,0.0))
+
LaunchedEffect(viewModel.effect) {
viewModel.effect.onEach {
if(it is LocationTrackingContract.Effect.Toast) {
@@ -84,9 +92,17 @@ internal fun LocationTrackingScreen() {
}
}
- LaunchedEffect(currentState.locationLatLng) {
- if(null == currentState.locationLatLng) return@LaunchedEffect
- cameraPosition.move(CameraUpdateFactory.newLatLng(currentState.locationLatLng))
+ LaunchedEffect(Unit) {
+ snapshotFlow { currentState.locationLatLng }.collect { latLng ->
+ latLng?.let {
+ locationIconState.position = it
+ if(!isRenderLocation) {
+ isRenderLocation = true
+ // 确保首次需要动画位移到当前用户的位置
+ cameraPosition.move(CameraUpdateFactory.newLatLng(it))
+ }
+ }
+ }
}
LaunchedEffect(currentState.isOpenGps, reqGPSPermission.allPermissionsGranted) {
@@ -115,6 +131,24 @@ internal fun LocationTrackingScreen() {
properties = currentState.mapProperties,
uiSettings = currentState.mapUiSettings,
locationSource = viewModel
- )
+ ){
+ // 【建议】:不使用有角度方向的图标,使用圆形图标加阴影效果比这个好多了
+ // 这里不用腾讯自己的定位蓝点样式
+ if(locationIconState.position.latitude > 0){
+ Circle(
+ center = locationIconState.position,
+ fillColor = Color(0x801A9CE2),
+ strokeColor = Color(0xFF1A9CE2),
+ strokeWidth = 1F,
+ radius = currentState.locationCircleRadius.toDouble()
+ )
+ Marker(
+ anchor = Offset(0.5F,0.5F),
+ rotation = currentState.currentRotation,
+ state = locationIconState,
+ icon = BitmapDescriptorFactory.fromResource(R.drawable.ic_map_location_self)
+ )
+ }
+ }
}
}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LogisticsScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LogisticsScreen.kt
new file mode 100644
index 0000000..92cf919
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/LogisticsScreen.kt
@@ -0,0 +1,90 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.sample.common.utils.showToast
+import com.melody.tencentmap.myapplication.contract.LogisticsContract
+import com.melody.tencentmap.myapplication.ui.route.LogisticsRouteOverlayContent
+import com.melody.tencentmap.myapplication.viewmodel.LogisticsViewModel
+import com.melody.ui.components.RedCenterLoading
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * LogisticsScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 13:40
+ */
+@Composable
+internal fun LogisticsScreen() {
+ val viewModel: LogisticsViewModel = viewModel()
+ val cameraPositionState = rememberCameraPositionState()
+ val currentState by viewModel.uiState.collectAsState()
+
+ LaunchedEffect(currentState.routePlanDataState) {
+ currentState.routePlanDataState?.let {
+ // 模仿pdd物流详情页,显示范围
+ cameraPositionState.move(CameraUpdateFactory.newLatLngBounds(it.latLngBounds, 300))
+ // 目前腾讯地图的bug,有几率move过程中,首次添加polyline,出现末尾大范围断线的问题
+ viewModel.fixPolylineRainbowBug()
+ }
+ }
+
+ LaunchedEffect(viewModel.effect) {
+ viewModel.effect.onEach {
+ if(it is LogisticsContract.Effect.Toast) {
+ showToast(it.msg)
+ }
+ }.collect()
+ }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ properties = currentState.mapProperties,
+ uiSettings = currentState.uiSettings,
+ onMapLoaded = viewModel::queryRoutePlan
+ ) {
+ if(null != currentState.routePlanDataState && currentState.fixPolylineRainbow) {
+ LogisticsRouteOverlayContent(currentState.routePlanDataState!!)
+ }
+ }
+ if(currentState.isLoading) {
+ RedCenterLoading()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerAnimationScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerAnimationScreen.kt
new file mode 100644
index 0000000..899aacf
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerAnimationScreen.kt
@@ -0,0 +1,91 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.overlay.MarkerInfoWindowContent
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.tencentmap.myapplication.viewmodel.MarkerAnimationViewModel
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+
+/**
+ * MarkerAnimationScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 15:33
+ */
+@Composable
+internal fun MarkerAnimationScreen() {
+ val viewModel: MarkerAnimationViewModel = viewModel()
+ val uiState by viewModel.uiState.collectAsState()
+ val cameraPositionState = rememberCameraPositionState()
+ val markerState = rememberMarkerState(position = uiState.markerDefaultLocation)
+
+ LaunchedEffect(uiState.mapLoaded) {
+ if(uiState.mapLoaded) {
+ markerState.showInfoWindow()
+ }
+ }
+
+ LaunchedEffect(uiState.markerDefaultLocation) {
+ cameraPositionState.animate(CameraUpdateFactory.newLatLng(uiState.markerDefaultLocation))
+ }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ uiSettings = uiState.mapUiSettings,
+ onMapLoaded = viewModel::handleMapLoaded
+ ) {
+ MarkerInfoWindowContent(
+ state = markerState,
+ animation = uiState.markerAnimation,
+ content = {
+ Text(
+ modifier = Modifier.padding(4.dp),
+ text = "点击Marker查看动画"
+ )
+ },
+ onAnimationEnd = viewModel::finishMarkerAnimation,
+ onClick = {
+ viewModel.startMarkerAnimation()
+ true
+ }
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerClusterScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerClusterScreen.kt
new file mode 100644
index 0000000..61cbdbf
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MarkerClusterScreen.kt
@@ -0,0 +1,104 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material.Card
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.overlay.ClusterOverlay
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.sample.common.utils.showToast
+import com.melody.tencentmap.myapplication.model.MapClusterItem
+import com.melody.tencentmap.myapplication.viewmodel.MarkerClusterViewModel
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+
+/**
+ * MarkerClusterScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 09:46
+ */
+@Composable
+internal fun MarkerClusterScreen() {
+ val viewModel: MarkerClusterViewModel = viewModel()
+ val uiState by viewModel.uiState.collectAsState()
+ val cameraPositionState = rememberCameraPositionState()
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ uiSettings = uiState.mapUiSettings
+ ) {
+ uiState.mapClusterItems?.let { items ->
+ ClusterOverlay(
+ clusterItems = items,
+ clusterColor = Color(0xFF5AC95A),
+ // 不设置clusterItemIcon,会使用SDK默认的图标
+ clusterItemIcon = BitmapDescriptorFactory.fromAsset("red_marker.png"),
+ onClusterItemClick = {
+ showToast("单个Marker被点击:"+it?.position.toString())
+ },
+ onClustersClick = {
+ // 你也可以,在这里自行缩放地图,触发:聚合点展开,懂?
+ showToast("聚合点被点击:"+it?.position.toString())
+ },
+ // 不需要InfoWindow,就不设置,懂?
+ onClustersInfoWindow = null/*{ cluster ->
+ Card(modifier = Modifier.size(100.dp).background(Color.White).padding(16.dp)) {
+ Text(text = "我是聚合点的InfoWindow")
+ }
+ }*/,
+ onClusterItemInfoWindow = { clusterItem ->
+ Card(modifier = Modifier
+ .width(100.dp)
+ .wrapContentHeight()
+ .background(Color.White)
+ .padding(16.dp)) {
+ if(clusterItem is MapClusterItem) {
+ // TAG数据,根据你自己的业务去做定制,懂?
+ Text(text = "我是单个Marker的ItemInfoWindow,获取TAG数据:${clusterItem.tag.toString()}")
+ }else {
+ // .....
+ }
+ }
+ }
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen.kt
new file mode 100644
index 0000000..4cfa025
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen.kt
@@ -0,0 +1,73 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.overlay.Polyline
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.tencentmap.myapplication.viewmodel.MovementTrackViewModel
+import com.melody.ui.components.RedCenterLoading
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import kotlinx.coroutines.flow.filterNotNull
+
+/**
+ * MovementTrackScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:33
+ */
+@Composable
+internal fun MovementTrackScreen() {
+ val viewModel: MovementTrackViewModel = viewModel()
+ val currentState by viewModel.uiState.collectAsState()
+ val cameraPositionState = rememberCameraPositionState()
+ LaunchedEffect(Unit) {
+ snapshotFlow { currentState.trackLatLng }.filterNotNull().collect {
+ cameraPositionState.animate(CameraUpdateFactory.newLatLngBounds(it, 200))
+ }
+ }
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ uiSettings = currentState.uiSettings,
+ onMapLoaded = viewModel::loadMovementTrackData
+ ){
+ Polyline(points = currentState.latLngList, polylineColor = Color(0xFFF38D0F))
+ }
+ if(currentState.isLoading) {
+ RedCenterLoading()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen2.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen2.kt
new file mode 100644
index 0000000..19e541f
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/MovementTrackScreen2.kt
@@ -0,0 +1,90 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.overlay.MovingPointOverlay
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.viewmodel.MovementTrackViewModel
+import com.melody.ui.components.RedCenterLoading
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+import kotlinx.coroutines.flow.filterNotNull
+
+/**
+ * MovementTrackScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:33
+ */
+@Composable
+internal fun MovementTrackScreen2() {
+ val viewModel: MovementTrackViewModel = viewModel()
+ val currentState by viewModel.uiState.collectAsState()
+ val cameraPositionState = rememberCameraPositionState()
+ LaunchedEffect(Unit) {
+ snapshotFlow { currentState.trackLatLng }.filterNotNull().collect {
+ cameraPositionState.animate(CameraUpdateFactory.newLatLngBounds(it, 200))
+ }
+ }
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ uiSettings = currentState.uiSettings,
+ onMapLoaded = viewModel::loadMovementTrackData
+ ){
+ PolylineRainbow(
+ points = currentState.latLngList,
+ width = 15F,
+ isLineCap = true,
+ useGradient = true,
+ rainbow = currentState.polylineRainbow,
+ animation = currentState.polylineAnimation
+ )
+ if(currentState.latLngList.isNotEmpty()) {
+ MovingPointOverlay(
+ points = currentState.latLngList,
+ icon = BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher),
+ isStartSmoothMove = true,
+ totalDuration = currentState.polylineAnimDuration
+ )
+ }
+ }
+ if(currentState.isLoading) {
+ RedCenterLoading()
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/OverlayScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/OverlayScreen.kt
index d1d1d85..1953ff7 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/OverlayScreen.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/OverlayScreen.kt
@@ -54,6 +54,7 @@ import com.melody.map.tencent_compose.overlay.MarkerInfoWindowContent
import com.melody.map.tencent_compose.overlay.Polygon
import com.melody.map.tencent_compose.overlay.PolygonBorder
import com.melody.map.tencent_compose.overlay.Polyline
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
import com.melody.map.tencent_compose.overlay.TileOverlay
import com.melody.map.tencent_compose.overlay.rememberMarkerState
import com.melody.map.tencent_compose.position.rememberCameraPositionState
@@ -168,10 +169,11 @@ internal fun OverlayScreen() {
)
// 线段的出现动画
- Polyline(
+ PolylineRainbow(
points = currentState.polylineAnimPointList,
width = 15F,
isLineCap = true,
+ useGradient = false,
// 彩虹线段...
rainbow = currentState.polylineRainbow,
animation = currentState.polylineAnimation,
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/RoutePlanScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/RoutePlanScreen.kt
new file mode 100644
index 0000000..50f7649
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/RoutePlanScreen.kt
@@ -0,0 +1,137 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.google.accompanist.flowlayout.FlowRow
+import com.melody.map.tencent_compose.TXMap
+import com.melody.map.tencent_compose.position.rememberCameraPositionState
+import com.melody.sample.common.utils.showToast
+import com.melody.tencentmap.myapplication.contract.RoutePlanContract
+import com.melody.tencentmap.myapplication.model.BusRouteDataState
+import com.melody.tencentmap.myapplication.model.DrivingRouteDataState
+import com.melody.tencentmap.myapplication.model.RideRouteDataState
+import com.melody.tencentmap.myapplication.model.WalkRouteDataState
+import com.melody.tencentmap.myapplication.ui.route.BusRouteOverlayContent
+import com.melody.tencentmap.myapplication.ui.route.DrivingRouteOverlayContent
+import com.melody.tencentmap.myapplication.ui.route.RideRouteOverlayContent
+import com.melody.tencentmap.myapplication.ui.route.WalkingRouteOverlayContent
+import com.melody.tencentmap.myapplication.viewmodel.RoutePlanViewModel
+import com.melody.ui.components.MapMenuButton
+import com.melody.ui.components.RedCenterLoading
+import com.melody.ui.components.RoadTrafficSwitch
+import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * RoutePlanScreen
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 16:40
+ */
+@Composable
+internal fun RoutePlanScreen() {
+ val viewModel: RoutePlanViewModel = viewModel()
+ val cameraPositionState = rememberCameraPositionState()
+ val currentState by viewModel.uiState.collectAsState()
+
+ LaunchedEffect(viewModel.effect) {
+ viewModel.effect.onEach {
+ if(it is RoutePlanContract.Effect.Toast) {
+ showToast(it.msg)
+ }
+ }.collect()
+ }
+
+ LaunchedEffect(currentState.routePlanDataState?.latLngBounds) {
+ currentState.routePlanDataState?.latLngBounds?.let {
+ cameraPositionState.move(CameraUpdateFactory.newLatLngBounds(it, 100))
+ }
+ }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ TXMap(
+ modifier = Modifier.matchParentSize(),
+ cameraPositionState = cameraPositionState,
+ properties = currentState.mapProperties,
+ uiSettings = currentState.uiSettings,
+ onMapLoaded = viewModel::queryRoutePlan
+ ) {
+ when(currentState.routePlanDataState) {
+ is DrivingRouteDataState -> {
+ val dataState = currentState.routePlanDataState as DrivingRouteDataState
+ DrivingRouteOverlayContent(dataState)
+ }
+ is BusRouteDataState -> {
+ val dataState = currentState.routePlanDataState as BusRouteDataState
+ BusRouteOverlayContent(dataState)
+ }
+ is WalkRouteDataState -> {
+ val dataState = currentState.routePlanDataState as WalkRouteDataState
+ WalkingRouteOverlayContent(dataState)
+ }
+ is RideRouteDataState -> {
+ val dataState = currentState.routePlanDataState as RideRouteDataState
+ RideRouteOverlayContent(dataState)
+ }
+ }
+ }
+ if(currentState.isLoading) {
+ RedCenterLoading()
+ }
+ MenuButtonList(viewModel::queryRoutePlan)
+ RoadTrafficSwitch(
+ modifier = Modifier
+ .align(Alignment.CenterEnd)
+ .padding(end = 4.dp)
+ .clickable(onClick = viewModel::switchRoadTraffic),
+ isEnable = currentState.mapProperties.isTrafficEnabled
+ )
+ }
+}
+
+@Composable
+private fun BoxScope.MenuButtonList(onClick:(Int) -> Unit) {
+ val currentOnClick by rememberUpdatedState(newValue = onClick)
+ FlowRow(
+ modifier = Modifier
+ .align(Alignment.TopCenter)
+ .fillMaxWidth()
+ .background(Color.Black.copy(alpha = 0.3F))
+ ) {
+ MapMenuButton(text = "驾车", onClick = { currentOnClick.invoke(0) })
+ MapMenuButton(text = "公交", onClick = { currentOnClick.invoke(1) })
+ MapMenuButton(text = "步行", onClick = { currentOnClick.invoke(2) })
+ MapMenuButton(text = "骑行", onClick = { currentOnClick.invoke(3) })
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/SmoothMoveScreen.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/SmoothMoveScreen.kt
index 2f8970d..b255e8d 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/SmoothMoveScreen.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/SmoothMoveScreen.kt
@@ -33,9 +33,7 @@ import androidx.compose.ui.graphics.Color
import androidx.lifecycle.viewmodel.compose.viewModel
import com.melody.map.tencent_compose.TXMap
import com.melody.map.tencent_compose.overlay.MovingPointOverlay
-import com.melody.map.tencent_compose.overlay.Polyline
import com.melody.map.tencent_compose.overlay.PolylineCustomTexture
-import com.melody.map.tencent_compose.overlay.PolylineRainbow
import com.melody.map.tencent_compose.position.rememberCameraPositionState
import com.melody.sample.common.utils.showToast
import com.melody.tencentmap.myapplication.contract.SmoothMoveContract
@@ -43,7 +41,6 @@ import com.melody.tencentmap.myapplication.viewmodel.SmoothMoveViewModel
import com.melody.ui.components.MapMenuButton
import com.melody.ui.components.RedCenterLoading
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory
-import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
@@ -81,7 +78,7 @@ internal fun SmoothMoveScreen() {
onMapLoaded = viewModel::handleMapLoaded
) {
if(currentState.trackPoints.isNotEmpty()) {
- Polyline(
+ PolylineCustomTexture(
points = currentState.trackPoints,
polylineColor = Color(0xFF0492FF),
customTexture_stable = PolylineCustomTexture.create(arrowSpacing = 30, arrowTexture = currentState.bitmapTexture),
@@ -105,6 +102,7 @@ internal fun SmoothMoveScreen() {
.background(Color.Black.copy(alpha = 0.3F))){
MapMenuButton(
modifier = Modifier.align(Alignment.Center),
+ // 腾讯地图的Smooth,不能真正的暂停和恢复,因为小车方向角度会失效,所以这里提供的示例也只有停止和开始
text = if (currentState.isStart) "停止" else "开始",
onClick = viewModel::toggle
)
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/BusRouteOverlayContent.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/BusRouteOverlayContent.kt
new file mode 100644
index 0000000..7a01881
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/BusRouteOverlayContent.kt
@@ -0,0 +1,114 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui.route
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.Polyline
+import com.melody.map.tencent_compose.overlay.PolylineCustomTexture
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.model.BusRouteDataState
+import com.tencent.lbssearch.`object`.result.TransitResultObject
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions
+
+/**
+ * BusRouteOverlayContent
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/20 15:44
+ */
+@TXMapComposable
+@Composable
+internal fun BusRouteOverlayContent(dataState: BusRouteDataState) {
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+ dataState.routeList.forEachIndexed { index, route ->
+ route.steps.forEach { segment ->
+ if (segment is TransitResultObject.Transit) {
+ PolylineCustomTexture(
+ points = segment.lines[0].polyline,
+ width = dataState.polylineWidth,
+ borderWidth = dataState.polylineBorderWidth,
+ animation = null,
+ isLineCap = true,
+ zIndex = if(selectedIndex == index) 2F else 1F,
+ polylineColor = if(selectedIndex == index) Color(0xFF58C180) else Color(0xFF83DAA4),
+ polylineBorderColor = if(selectedIndex == index) Color(0xFF387C54) else Color(0xFF76BB93),
+ customTexture_stable = PolylineCustomTexture.create(
+ arrowSpacing = 80, arrowTexture = BitmapDescriptorFactory.fromResource(
+ R.drawable.color_arrow_texture
+ )
+ ),
+ onClick = {
+ selectedIndex = index
+ }
+ )
+ } else if (segment is TransitResultObject.Walking) {
+ Polyline(
+ isLineCap = true,
+ polylineColor = if(selectedIndex == index) Color(0xFFFF0404) else Color(0xFFEE8D8D),
+ polylineBorderColor = if(selectedIndex == index) Color(0xFFFF0404) else Color(0xFFEE8D8D),
+ borderWidth = 1F,
+ points = segment.polyline,
+ lineType = PolylineOptions.LineType.LINE_TYPE_IMAGEINARYLINE,
+ pattern = listOf(35, 20),
+ zIndex = if(selectedIndex == index) 3F else 2F,
+ onClick = {
+ selectedIndex = index
+ }
+ )
+ }
+ }
+ }
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_start_guide_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_end_guide_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_start_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_end_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/DrivingRouteOverlayContent.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/DrivingRouteOverlayContent.kt
new file mode 100644
index 0000000..4993bba
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/DrivingRouteOverlayContent.kt
@@ -0,0 +1,105 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui.route
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.PolylineCustomTexture
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.model.DrivingRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+
+/**
+ * DrivingRouteOverlayContent
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/20 15:25
+ */
+@TXMapComposable
+@Composable
+internal fun DrivingRouteOverlayContent(dataState: DrivingRouteDataState) {
+ var visibleStart by rememberSaveable { mutableStateOf(false) }
+ var visibleEnd by rememberSaveable { mutableStateOf(false) }
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+
+ dataState.points.forEachIndexed { index, pointList ->
+ PolylineCustomTexture(
+ isLineCap = true,
+ points = pointList,
+ width = dataState.polylineWidth,
+ borderWidth = dataState.polylineBorderWidth,
+ animation = dataState.polylineAnim,
+ zIndex = if(selectedIndex == index) 2F else 1F,
+ polylineColor = if(selectedIndex == index) Color(0xFF58C180) else Color(0xFF83DAA4),
+ polylineBorderColor = if(selectedIndex == index) Color(0xFF387C54) else Color(0xFF76BB93),
+ customTexture_stable = PolylineCustomTexture.create(arrowSpacing = 80, arrowTexture = BitmapDescriptorFactory.fromResource(
+ R.drawable.color_arrow_texture
+ )),
+ onAnimationStart = {
+ visibleStart = true
+ visibleEnd = false
+ },
+ onAnimationEnd = {
+ visibleEnd = true
+ },
+ onClick = {
+ selectedIndex = index
+ }
+ )
+ }
+ Marker(
+ visible = visibleStart,
+ anchor = Offset(0.5f,0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_start_guide_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ visible = visibleEnd,
+ anchor = Offset(0.5f,0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_end_guide_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+ Marker(
+ visible = visibleStart,
+ anchor = Offset(0.5F,1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_start_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ visible = visibleEnd,
+ anchor = Offset(0.5F,1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_end_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/LogisticsRouteOverlayContent.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/LogisticsRouteOverlayContent.kt
new file mode 100644
index 0000000..c90e277
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/LogisticsRouteOverlayContent.kt
@@ -0,0 +1,151 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui.route
+
+import android.graphics.Color
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.viewinterop.AndroidView
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.melody.map.tencent_compose.overlay.MarkerInfoWindow
+import com.melody.map.tencent_compose.overlay.PolylineRainbow
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.model.LogisticsRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+
+/**
+ * LogisticsRouteOverlayContent
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 14:10
+ */
+@TXMapComposable
+@Composable
+internal fun LogisticsRouteOverlayContent(dataState: LogisticsRouteDataState) {
+ val startState = rememberMarkerState(position = dataState.startPoint)
+ val carState = rememberMarkerState(position = dataState.carLocation)
+ val endState = rememberMarkerState(position = dataState.endPoint)
+
+ LaunchedEffect(Unit) {
+ startState.showInfoWindow()
+ carState.showInfoWindow()
+ endState.showInfoWindow()
+ }
+
+ PolylineRainbow(
+ isLineCap = true,
+ useGradient = false,
+ points = dataState.points,
+ rainbow = dataState.rainbow
+ )
+
+ MarkerInfoWindow(
+ anchor = Offset(0.5f,1f),
+ draggable = true,
+ icon = BitmapDescriptorFactory.fromResource(R.drawable.ic_pdd_fahuo_location),
+ state = startState,
+ zIndex = 0F,
+ // 根据你自己业务获取
+ title = "上海市",
+ tag = R.drawable.ic_pdd_fahuo,
+ content = { marker ->
+ LogisticsMarkInfoWindowContent(marker.tag as Int, marker.title)
+ }
+ )
+
+ MarkerInfoWindow(
+ anchor = Offset(0.5f,1f),
+ draggable = true,
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(R.drawable.ic_pdd_shouhuo_dark_location),
+ state = endState,
+ title = "收货地址",
+ tag = R.drawable.ic_pdd_shou_huo,
+ content = { marker ->
+ LogisticsMarkInfoWindowContent(marker.tag as Int, marker.title)
+ }
+ )
+
+ MarkerInfoWindow(
+ anchor = Offset(0.5f,0.5f),
+ draggable = true,
+ icon = BitmapDescriptorFactory.fromAsset("ic_pdd_car.png"),
+ state = carState,
+ zIndex = 2F,
+ rotation = dataState.carRotation.toFloat(),
+ title = "派件中,预计后天送达",
+ snippet = "当前在宜春市",
+ tag = R.drawable.ic_pdd_transit,
+ content = { marker ->
+ LogisticsMarkInfoWindowContent2(marker.tag as Int, marker.title,marker.snippet)
+ }
+ )
+}
+
+@Composable
+private fun LogisticsMarkInfoWindowContent(resource: Int, title: String) {
+ AndroidView(
+ modifier = Modifier.wrapContentSize(),
+ factory = {
+ TextView(it).apply {
+ setTextColor(Color.BLACK)
+ }
+ }
+ )
+ {
+ it.setBackgroundResource(resource)
+ it.text = title
+ }
+}
+
+@Composable
+private fun LogisticsMarkInfoWindowContent2(resource: Int, title: String, snippet: String) {
+ AndroidView(
+ modifier = Modifier.wrapContentSize(),
+ factory = {
+ LinearLayout(it).apply {
+ orientation = LinearLayout.VERTICAL
+ addView(TextView(it).apply {
+ setTextColor(Color.WHITE)
+ textSize = 16F
+ })
+ addView(TextView(it).apply {
+ setTextColor(Color.WHITE)
+ textSize = 12F
+ })
+ }
+ }
+ )
+ {
+ it.setBackgroundResource(resource)
+ (it.getChildAt(0) as TextView).text = title
+ (it.getChildAt(1) as TextView).text = snippet
+ }
+}
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/RideRouteOverlayContent.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/RideRouteOverlayContent.kt
new file mode 100644
index 0000000..9a6fad8
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/RideRouteOverlayContent.kt
@@ -0,0 +1,92 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui.route
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.PolylineCustomTexture
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.tencentmap.myapplication.R
+import com.melody.tencentmap.myapplication.model.RideRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+
+/**
+ * RideRouteOverlayContent
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/22 11:27
+ */
+@TXMapComposable
+@Composable
+internal fun RideRouteOverlayContent(dataState: RideRouteDataState) {
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+ dataState.ridePoints.forEachIndexed { index, pointList ->
+ PolylineCustomTexture(
+ points = pointList,
+ width = dataState.polylineWidth,
+ borderWidth = dataState.polylineBorderWidth,
+ isLineCap = true,
+ zIndex = if(selectedIndex == index) 2F else 1F,
+ polylineColor = if(selectedIndex == index) Color(0xFF58C180) else Color(0xFF83DAA4),
+ polylineBorderColor = if(selectedIndex == index) Color(0xFF387C54) else Color(0xFF76BB93),
+ customTexture_stable = PolylineCustomTexture.create(
+ arrowSpacing = 80, arrowTexture = BitmapDescriptorFactory.fromResource(
+ R.drawable.color_arrow_texture
+ )
+ ),
+ onClick = {
+ selectedIndex = index
+ }
+ )
+ }
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_start_guide_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_end_guide_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_start_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_end_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/WalkingRouteOverlayContent.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/WalkingRouteOverlayContent.kt
new file mode 100644
index 0000000..b695bf1
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/ui/route/WalkingRouteOverlayContent.kt
@@ -0,0 +1,89 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.ui.route
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.melody.map.tencent_compose.overlay.Marker
+import com.melody.map.tencent_compose.overlay.Polyline
+import com.melody.map.tencent_compose.overlay.rememberMarkerState
+import com.melody.tencentmap.myapplication.model.WalkRouteDataState
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory
+import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions
+
+/**
+ * WalkingRouteOverlayContent
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/22 11:26
+ */
+@TXMapComposable
+@Composable
+internal fun WalkingRouteOverlayContent(dataState: WalkRouteDataState) {
+ var selectedIndex by rememberSaveable { mutableStateOf(0) }
+ dataState.wakingPoints.forEachIndexed { index, pointList ->
+ Polyline(
+ isLineCap = true,
+ points = pointList,
+ width = dataState.polylineWidth,
+ polylineColor = if(selectedIndex == index) Color(0xFFFF0404) else Color(0xFFEE8D8D),
+ polylineBorderColor = if(selectedIndex == index) Color(0xFFFF0404) else Color(0xFFEE8D8D),
+ borderWidth = 1F,
+ zIndex = if(selectedIndex == index) 2F else 1F,
+ lineType = PolylineOptions.LineType.LINE_TYPE_IMAGEINARYLINE,
+ pattern = listOf(35, 20),
+ onClick = {
+ selectedIndex = index
+ }
+ )
+ }
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_start_guide_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5f, 0.5f),
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.ic_map_end_guide_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_start_icon),
+ state = rememberMarkerState(position = dataState.startPoint)
+ )
+ Marker(
+ anchor = Offset(0.5F, 1F),
+ zIndex = 1F,
+ icon = BitmapDescriptorFactory.fromResource(com.melody.ui.components.R.drawable.bus_end_icon),
+ state = rememberMarkerState(position = dataState.endPoint)
+ )
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/DragDropSelectPointViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/DragDropSelectPointViewModel.kt
new file mode 100644
index 0000000..53b607d
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/DragDropSelectPointViewModel.kt
@@ -0,0 +1,180 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import android.content.Intent
+import android.os.Looper
+import android.provider.Settings
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.result.ActivityResult
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.sample.common.model.ISensorDegreeListener
+import com.melody.sample.common.utils.SDKUtils
+import com.melody.sample.common.utils.SensorEventHelper
+import com.melody.sample.common.utils.openAppPermissionSettingPage
+import com.melody.sample.common.utils.safeLaunch
+import com.melody.tencentmap.myapplication.contract.DragDropSelectPointContract
+import com.melody.tencentmap.myapplication.repo.DragDropSelectPointRepository
+import com.tencent.lbssearch.`object`.result.SearchResultObject
+import com.tencent.map.geolocation.TencentLocation
+import com.tencent.map.geolocation.TencentLocationListener
+import com.tencent.map.geolocation.TencentLocationManager
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import kotlinx.coroutines.Dispatchers
+import java.util.UUID
+
+/**
+ * DragDropSelectPointViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 09:16
+ */
+class DragDropSelectPointViewModel :
+ BaseViewModel(),
+ TencentLocationListener, ISensorDegreeListener {
+
+ private val sensorEventHelper = SensorEventHelper()
+
+ override fun createInitialState(): DragDropSelectPointContract.State {
+ return DragDropSelectPointContract.State(
+ isClickForceStartLocation = false,
+ isShowOpenGPSDialog = false,
+ isOpenGps = null,
+ currentLocation = null,
+ currentRotation = 0F,
+ poiItems = null
+ )
+ }
+
+ override fun handleEvents(event: DragDropSelectPointContract.Event) {
+ when(event) {
+ is DragDropSelectPointContract.Event.ShowOpenGPSDialog -> {
+ setState { copy(isShowOpenGPSDialog = true) }
+ }
+ is DragDropSelectPointContract.Event.HideOpenGPSDialog -> {
+ setState { copy(isShowOpenGPSDialog = false) }
+ }
+ }
+ }
+
+ init {
+ // 这里,可以在应用初始化的时候,缓存下来,下次直接使用,保证唯一性即可,毕竟获取手机通讯录权限是隐私权限
+ // 上传设备唯一标识,用于在定位发生问题查询问题原因
+ TencentLocationManager.getInstance(SDKUtils.getApplicationContext())
+ .setDeviceID(SDKUtils.getApplicationContext(), "id" + UUID.randomUUID())
+ checkGpsStatus()
+ sensorEventHelper.registerSensorListener(this)
+ }
+
+ fun checkGpsStatus() = asyncLaunch(Dispatchers.IO) {
+ val isOpenGps = DragDropSelectPointRepository.checkGPSIsOpen()
+ setState { copy(isOpenGps = isOpenGps) }
+ if(!isOpenGps) {
+ setEvent(DragDropSelectPointContract.Event.ShowOpenGPSDialog)
+ } else {
+ hideOpenGPSDialog()
+ }
+ }
+
+ fun hideOpenGPSDialog() {
+ setEvent(DragDropSelectPointContract.Event.HideOpenGPSDialog)
+ }
+
+ fun startMapLocation() = asyncLaunch(Dispatchers.IO) {
+ if(currentState.isClickForceStartLocation) return@asyncLaunch
+ setState { copy(isClickForceStartLocation = true) }
+ // 单次定位,不需要主动调用停止定位
+ TencentLocationManager.getInstance(SDKUtils.getApplicationContext())
+ .requestSingleFreshLocation(null, this@DragDropSelectPointViewModel, Looper.getMainLooper())
+ }
+
+ fun showSelectAddressInfo(poiItemData: SearchResultObject.SearchResultData) {
+ setEffect {
+ DragDropSelectPointContract.Effect.Toast(
+ "选择的地址是:".plus(poiItemData.title ?: "")
+ .plus(poiItemData.address ?: "")
+ )
+ }
+ }
+
+ /**
+ * 手机开了GPS,app没有授予权限
+ */
+ fun handleNoGrantLocationPermission() {
+ setEvent(DragDropSelectPointContract.Event.ShowOpenGPSDialog)
+ }
+
+ fun openGPSPermission(launcher: ManagedActivityResultLauncher) {
+ if(DragDropSelectPointRepository.checkGPSIsOpen()) {
+ // 已打开系统GPS,APP还没授权,跳权限页面
+ openAppPermissionSettingPage()
+ } else {
+ // 打开系统GPS开关页面
+ launcher.safeLaunch(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
+ }
+ }
+
+ override fun onCleared() {
+ sensorEventHelper.unRegisterSensorListener()
+ if(currentState.isClickForceStartLocation) {
+ TencentLocationManager.getInstance(SDKUtils.getApplicationContext()).removeUpdates(this)
+ }
+ super.onCleared()
+ }
+
+ /**
+ * 搜索当前位置附近1000米内的地址数据
+ */
+ fun doSearchQueryPoi(latLng: LatLng, cityName: String) = asyncLaunch(Dispatchers.IO) {
+ DragDropSelectPointRepository.queryPoiResult(
+ latLng = latLng,
+ cityName = cityName,
+ onSuccess = {
+ setState { copy(poiItems = it) }
+ },
+ onFailed = {
+ setEffect { DragDropSelectPointContract.Effect.Toast(it) }
+ })
+ }
+
+ override fun onSensorDegree(degree: Float) {
+ setState { copy(currentRotation = 360 - degree) }
+ }
+
+ override fun onLocationChanged(location: TencentLocation?, p1: Int, p2: String?) {
+ setState { copy(isClickForceStartLocation = false) }
+ if (null == location) {
+ setEffect { DragDropSelectPointContract.Effect.Toast("定位失败,请检查定位权限和网络....") }
+ return
+ }
+ val latitude = location.latitude
+ val longitude = location.longitude
+ setState { copy(currentLocation = LatLng(latitude, longitude)) }
+ doSearchQueryPoi(LatLng(latitude, longitude),location.city)
+ }
+
+ override fun onStatusUpdate(p0: String?, p1: Int, p2: String?) {
+ // ignore
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LocationTrackingViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LocationTrackingViewModel.kt
index 00dc29f..1d16a68 100644
--- a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LocationTrackingViewModel.kt
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LocationTrackingViewModel.kt
@@ -28,6 +28,8 @@ import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import com.melody.map.tencent_compose.poperties.MapProperties
import com.melody.sample.common.base.BaseViewModel
+import com.melody.sample.common.model.ISensorDegreeListener
+import com.melody.sample.common.utils.SensorEventHelper
import com.melody.sample.common.utils.openAppPermissionSettingPage
import com.melody.sample.common.utils.safeLaunch
import com.melody.tencentmap.myapplication.contract.LocationTrackingContract
@@ -50,16 +52,19 @@ import kotlinx.coroutines.Dispatchers
*/
class LocationTrackingViewModel :
BaseViewModel(),
- LocationSource,TencentLocationListener {
+ LocationSource,TencentLocationListener, ISensorDegreeListener {
private var mLocationChangedListener: LocationSource.OnLocationChangedListener? = null
private var mLocationManager: TencentLocationManager? = null
private var mLocationRequest: TencentLocationRequest? = null
+ private val sensorEventHelper = SensorEventHelper()
override fun createInitialState(): LocationTrackingContract.State {
return LocationTrackingContract.State(
mapProperties = MapProperties(),
mapUiSettings = LocationTrackingRepository.initMapUiSettings(),
+ locationCircleRadius = 0F,
+ currentRotation = 0F,
isShowOpenGPSDialog = false,
grantLocationPermission = false,
locationLatLng = null,
@@ -79,6 +84,7 @@ class LocationTrackingViewModel :
}
init {
+ sensorEventHelper.registerSensorListener(this)
LocationTrackingRepository.initLocation { manager,request ->
mLocationManager = manager
mLocationRequest = request
@@ -132,10 +138,17 @@ class LocationTrackingViewModel :
private fun updateMyLocationStyle() {
// 因为腾讯的BitmapDescriptor需要在获取到MapContext之后才能用,否则会返回null,会导致定位蓝点图标无法修改生效
val myLocationStyle = LocationTrackingRepository.initMyLocationStyle()
- // 打开定位图层,修改定位样式
- setState { copy(mapProperties = mapProperties.copy(isMyLocationEnabled = true, myLocationStyle = myLocationStyle)) }
- // 显示定位按钮
- setState { copy(mapUiSettings = mapUiSettings.copy(myLocationButtonEnabled = true)) }
+ setState {
+ copy(
+ // 打开定位图层,修改定位样式
+ mapProperties = mapProperties.copy(
+ isMyLocationEnabled = true,
+ myLocationStyle = myLocationStyle
+ ),
+ // 显示定位按钮
+ mapUiSettings = mapUiSettings.copy(myLocationButtonEnabled = true)
+ )
+ }
}
override fun deactivate() {
@@ -146,6 +159,7 @@ class LocationTrackingViewModel :
}
override fun onCleared() {
+ sensorEventHelper.unRegisterSensorListener()
deactivate()
super.onCleared()
}
@@ -157,7 +171,7 @@ class LocationTrackingViewModel :
locationChangedListener = mLocationChangedListener
) {
setState {
- copy(locationLatLng = LatLng(it.latitude, it.longitude))
+ copy(locationLatLng = LatLng(it.latitude, it.longitude),locationCircleRadius = it.accuracy)
}
}
}
@@ -178,4 +192,8 @@ class LocationTrackingViewModel :
setEffect { LocationTrackingContract.Effect.Toast(it) }
}
}
+
+ override fun onSensorDegree(degree: Float) {
+ setState { copy(currentRotation = 360 - degree) }
+ }
}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LogisticsViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LogisticsViewModel.kt
new file mode 100644
index 0000000..12fd67b
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/LogisticsViewModel.kt
@@ -0,0 +1,82 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.tencentmap.myapplication.contract.LogisticsContract
+import com.melody.tencentmap.myapplication.repo.LogisticsRepository
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import kotlinx.coroutines.Dispatchers
+
+/**
+ * LogisticsViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/23 13:40
+ */
+class LogisticsViewModel :
+ BaseViewModel() {
+ override fun createInitialState(): LogisticsContract.State {
+ return LogisticsContract.State(
+ isLoading = false,
+ fixPolylineRainbow = false,
+ fromPoint = LatLng(31.19668, 121.337601),
+ toPoint = LatLng(28.194668, 112.976868),
+ uiSettings = LogisticsRepository.initMapUiSettings(),
+ mapProperties = LogisticsRepository.initMapProperties(),
+ routePlanDataState = null
+ )
+ }
+
+ override fun handleEvents(event: LogisticsContract.Event) {
+ when (event) {
+ is LogisticsContract.Event.QueryRoutePlan -> {
+ asyncLaunch(Dispatchers.IO) {
+ setState { copy(isLoading = true, routePlanDataState = null) }
+ val routePlanResult = kotlin.runCatching {
+ LogisticsRepository.getRoutePlan(
+ fromPoint = currentState.fromPoint,
+ toPoint = currentState.toPoint
+ )
+ }
+ setState { copy(isLoading = false) }
+ if (routePlanResult.isSuccess) {
+ setState { copy(routePlanDataState = routePlanResult.getOrNull()) }
+ } else {
+ setEffect { LogisticsContract.Effect.Toast(routePlanResult.exceptionOrNull()?.message) }
+ }
+ }
+ }
+ }
+ }
+
+ fun queryRoutePlan() {
+ setEvent(LogisticsContract.Event.QueryRoutePlan)
+ }
+
+ fun fixPolylineRainbowBug() {
+ setState { copy(fixPolylineRainbow = true) }
+ }
+
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerAnimationViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerAnimationViewModel.kt
new file mode 100644
index 0000000..999faeb
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerAnimationViewModel.kt
@@ -0,0 +1,84 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.tencentmap.myapplication.contract.MarkerAnimationContract
+import com.melody.tencentmap.myapplication.repo.MarkerAnimationRepository
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+
+/**
+ * MarkerAnimationViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/28 16:31
+ */
+class MarkerAnimationViewModel :
+ BaseViewModel() {
+
+ override fun createInitialState(): MarkerAnimationContract.State {
+ return MarkerAnimationContract.State(
+ mapLoaded = false,
+ mapUiSettings = MarkerAnimationRepository.initMapUiSettings(),
+ markerDefaultLocation = LatLng(39.984108,116.307557),
+ markerAnimation = null
+ )
+ }
+
+ override fun handleEvents(event: MarkerAnimationContract.Event) {
+ if(event is MarkerAnimationContract.Event.StartMarkerAnimation) {
+ setState {
+ copy(markerAnimation = MarkerAnimationRepository.prepareMarkerAnimation(
+ LatLng(
+ markerDefaultLocation.latitude + 0.05,
+ markerDefaultLocation.longitude - 0.05
+ )
+ ))
+ }
+ } else if(event is MarkerAnimationContract.Event.FinishMarkerAnimation) {
+ setState {
+ copy(
+ markerAnimation = null,
+ markerDefaultLocation = LatLng(
+ markerDefaultLocation.latitude + 0.05,
+ markerDefaultLocation.longitude - 0.05
+ )
+ )
+ }
+ }
+ }
+
+ fun startMarkerAnimation() {
+ setEvent(MarkerAnimationContract.Event.StartMarkerAnimation)
+ }
+
+ fun finishMarkerAnimation() {
+ setEvent(MarkerAnimationContract.Event.FinishMarkerAnimation)
+ }
+
+ fun handleMapLoaded() {
+ setState { copy(mapLoaded = true) }
+ }
+
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerClusterViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerClusterViewModel.kt
new file mode 100644
index 0000000..e498749
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MarkerClusterViewModel.kt
@@ -0,0 +1,51 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.tencentmap.myapplication.contract.MarkerClusterContract
+import com.melody.tencentmap.myapplication.repo.MarkerClusterRepository
+
+/**
+ * MarkerClusterViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 09:37
+ */
+class MarkerClusterViewModel :
+ BaseViewModel() {
+ override fun createInitialState(): MarkerClusterContract.State {
+ return MarkerClusterContract.State(
+ mapUiSettings = MarkerClusterRepository.initMapUiSettings(),
+ mapClusterItems = null
+ )
+ }
+ override fun handleEvents(event: MarkerClusterContract.Event) {
+ }
+
+ init {
+ setState { copy(mapClusterItems = MarkerClusterRepository.initClusterItems()) }
+ }
+
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MovementTrackViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MovementTrackViewModel.kt
new file mode 100644
index 0000000..e5bd272
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/MovementTrackViewModel.kt
@@ -0,0 +1,66 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.tencentmap.myapplication.contract.MovementTrackContract
+import com.melody.tencentmap.myapplication.repo.MovementTrackRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+
+/**
+ * MovementTrackViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/16 16:35
+ */
+class MovementTrackViewModel :
+ BaseViewModel() {
+
+ override fun createInitialState(): MovementTrackContract.State {
+ return MovementTrackContract.State(
+ isLoading = false,
+ latLngList = emptyList(),
+ trackLatLng = null,
+ uiSettings = MovementTrackRepository.initMapUiSettings(),
+ polylineAnimDuration = 15 * 1000,
+ polylineRainbow = null,
+ polylineAnimation = null,
+ )
+ }
+
+ override fun handleEvents(event: MovementTrackContract.Event) {
+ }
+
+ fun loadMovementTrackData() = asyncLaunch(Dispatchers.IO) {
+ setState { copy(isLoading = true) }
+ val list = MovementTrackRepository.parseLocationsData("MovementTrackTrace.csv")
+ val trackLatLng = MovementTrackRepository.getTrackLatLngBounds(list)
+ val polyLineAnimation = MovementTrackRepository.initPolylineAnimation(list[0],currentState.polylineAnimDuration)
+ val polylineRainbow = MovementTrackRepository.initPolylineRainbow(list.size)
+ setState { copy(latLngList = list, trackLatLng = trackLatLng, polylineAnimation = polyLineAnimation,polylineRainbow = polylineRainbow) }
+ delay(300) // 绘制本身就很耗时,延迟300毫秒,取消Loading,如果是网络数据,可直接取消Loading,这里是因为本地数据读取太快
+ setState { copy(isLoading = false) }
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/RoutePlanViewModel.kt b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/RoutePlanViewModel.kt
new file mode 100644
index 0000000..0f32017
--- /dev/null
+++ b/sample-tencent/src/main/java/com/melody/tencentmap/myapplication/viewmodel/RoutePlanViewModel.kt
@@ -0,0 +1,85 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.tencentmap.myapplication.viewmodel
+
+import com.melody.sample.common.base.BaseViewModel
+import com.melody.tencentmap.myapplication.contract.RoutePlanContract
+import com.melody.tencentmap.myapplication.repo.RoutePlanRepository
+import com.tencent.tencentmap.mapsdk.maps.model.LatLng
+import kotlinx.coroutines.Dispatchers
+
+/**
+ * RoutePlanViewModel
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/02/17 16:42
+ */
+class RoutePlanViewModel :
+ BaseViewModel(){
+
+ override fun createInitialState(): RoutePlanContract.State {
+ return RoutePlanContract.State(
+ isLoading = false,
+ fromPoint = LatLng(30.558987,103.951446),
+ toPoint = LatLng(30.710472,104.106445),
+ uiSettings = RoutePlanRepository.initMapUiSettings(),
+ mapProperties = RoutePlanRepository.initMapProperties(),
+ routePlanDataState = null
+ )
+ }
+
+ override fun handleEvents(event: RoutePlanContract.Event) {
+ when(event) {
+ is RoutePlanContract.Event.RoadTrafficClick -> {
+ setState { copy(mapProperties = mapProperties.copy(isTrafficEnabled = !mapProperties.isTrafficEnabled)) }
+ }
+ is RoutePlanContract.Event.QueryRoutePlan -> {
+ asyncLaunch(Dispatchers.IO) {
+ setState { copy(isLoading = true, routePlanDataState = null) }
+ val drivingRoutePlanResult = kotlin.runCatching {
+ RoutePlanRepository.queryRoutePlan(
+ queryType = event.queryType,
+ fromPoint = currentState.fromPoint,
+ toPoint = currentState.toPoint
+ )
+ }
+ setState { copy(isLoading = false) }
+ if(drivingRoutePlanResult.isSuccess) {
+ setState { copy(routePlanDataState = drivingRoutePlanResult.getOrNull()) }
+ }else{
+ setEffect { RoutePlanContract.Effect.Toast(drivingRoutePlanResult.exceptionOrNull()?.message) }
+ }
+ }
+ }
+ }
+ }
+
+ fun queryRoutePlan(queryType: Int = 0) {
+ setEvent(RoutePlanContract.Event.QueryRoutePlan(queryType))
+ }
+
+ fun switchRoadTraffic() {
+ setEvent(RoutePlanContract.Event.RoadTrafficClick)
+ }
+}
\ No newline at end of file
diff --git a/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo.9.png b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo.9.png
new file mode 100644
index 0000000..acb9383
Binary files /dev/null and b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo.9.png differ
diff --git a/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo_location.png b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo_location.png
new file mode 100644
index 0000000..cdc9e8f
Binary files /dev/null and b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_fahuo_location.png differ
diff --git a/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shou_huo.9.png b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shou_huo.9.png
new file mode 100644
index 0000000..8fe3423
Binary files /dev/null and b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shou_huo.9.png differ
diff --git a/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shouhuo_dark_location.png b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shouhuo_dark_location.png
new file mode 100644
index 0000000..0646f2a
Binary files /dev/null and b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_shouhuo_dark_location.png differ
diff --git a/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_transit.9.png b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_transit.9.png
new file mode 100644
index 0000000..18b98b4
Binary files /dev/null and b/sample-tencent/src/main/res/drawable-xxhdpi/ic_pdd_transit.9.png differ
diff --git a/sample-tencent/src/main/res/drawable/ic_transparent_location.png b/sample-tencent/src/main/res/drawable/ic_transparent_location.png
new file mode 100644
index 0000000..e67aa0c
Binary files /dev/null and b/sample-tencent/src/main/res/drawable/ic_transparent_location.png differ
diff --git a/sample-tencent/src/main/res/drawable/purple_pin.png b/sample-tencent/src/main/res/drawable/purple_pin.png
new file mode 100644
index 0000000..605de36
Binary files /dev/null and b/sample-tencent/src/main/res/drawable/purple_pin.png differ
diff --git a/sample-tencent/src/main/res/values/arrays.xml b/sample-tencent/src/main/res/values/arrays.xml
index 3fb5808..d1585b8 100644
--- a/sample-tencent/src/main/res/values/arrays.xml
+++ b/sample-tencent/src/main/res/values/arrays.xml
@@ -6,9 +6,10 @@
- @string/tx_map_main_feature_item_blue_location
- @string/tx_map_main_feature_item_smooth_move
- @string/tx_map_main_feature_item_movement_track
+ - @string/tx_map_main_feature_item_movement_track2
- @string/tx_map_main_feature_item_drag_drop_select_point
- @string/tx_map_main_feature_item_route_plan
- - @string/tx_map_main_feature_item_multipoint_click
+ - @string/tx_map_main_feature_item_logistics
- @string/tx_map_main_feature_item_marker_animation
- @string/tx_map_main_feature_item_cluster_effect
diff --git a/sample-tencent/src/main/res/values/strings.xml b/sample-tencent/src/main/res/values/strings.xml
index cbe84aa..8e1a158 100644
--- a/sample-tencent/src/main/res/values/strings.xml
+++ b/sample-tencent/src/main/res/values/strings.xml
@@ -7,8 +7,9 @@
轨迹平滑(移动/回放)
Dragdrop拖拽选点
运动轨迹
+ 【播放】运动轨迹
路径规划
- 海量点点击
+ 物流订单
Marker动画
点聚合效果
diff --git a/sample-ui-components/build.gradle b/sample-ui-components/build.gradle
index 63c9bba..89eb24c 100644
--- a/sample-ui-components/build.gradle
+++ b/sample-ui-components/build.gradle
@@ -53,5 +53,6 @@ dependencies {
api "androidx.compose.ui:ui-tooling-preview:$compose_version"
api "androidx.compose.ui:ui:$compose_version"
api "com.google.accompanist:accompanist-flowlayout:0.26.2-beta"
+ api "com.google.accompanist:accompanist-drawablepainter:0.26.2-beta"
implementation project(path: ':sample-common')
}
\ No newline at end of file
diff --git a/tencent-map-compose/build.gradle b/tencent-map-compose/build.gradle
index 26730a2..671d8a0 100644
--- a/tencent-map-compose/build.gradle
+++ b/tencent-map-compose/build.gradle
@@ -45,9 +45,9 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
// 地图库
- api 'com.tencent.map:tencent-map-vector-sdk:4.5.10.1'
+ api 'com.tencent.map:tencent-map-vector-sdk:4.5.12'
// 地图组件库,包括小车平移、点聚合等组件功能。
- api 'com.tencent.map:sdk-utilities:1.0.7'
+ api 'com.tencent.map:sdk-utilities:1.0.8'
// 定位SDK
- api 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.4.9.3'
+ api 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.5.0'
}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/android/support/v4/math/MathUtils.kt b/tencent-map-compose/src/main/java/android/support/v4/math/MathUtils.kt
new file mode 100644
index 0000000..8624403
--- /dev/null
+++ b/tencent-map-compose/src/main/java/android/support/v4/math/MathUtils.kt
@@ -0,0 +1,270 @@
+package android.support.v4.math
+
+/**
+ * 处理:腾讯地图[com.tencent.tencentmap.mapsdk.vector.utils.animation.MarkerTranslateAnimator.setAnimatorPosition]方法中需要用到support.v4的MathUtils
+ */
+@Suppress("unused")
+object MathUtils {
+ /**
+ * See [Math.addExact].
+ */
+ @JvmStatic
+ fun addExact(x: Int, y: Int): Int {
+ // copied from Math.java
+ val r = x + y
+ // HD 2-12 Overflow iff both arguments have the opposite sign of the result
+ if (x xor r and (y xor r) < 0) {
+ throw ArithmeticException("integer overflow")
+ }
+ return r
+ }
+
+ /**
+ * See [Math.addExact].
+ */
+ @JvmStatic
+ fun addExact(x: Long, y: Long): Long {
+ // copied from Math.java
+ val r = x + y
+ // HD 2-12 Overflow iff both arguments have the opposite sign of the result
+ if (x xor r and (y xor r) < 0) {
+ throw ArithmeticException("long overflow")
+ }
+ return r
+ }
+
+ /**
+ * See [Math.subtractExact].
+ */
+ @JvmStatic
+ fun subtractExact(x: Int, y: Int): Int {
+ // copied from Math.java
+ val r = x - y
+ // HD 2-12 Overflow iff the arguments have different signs and
+ // the sign of the result is different than the sign of x
+ if (x xor y and (x xor r) < 0) {
+ throw ArithmeticException("integer overflow")
+ }
+ return r
+ }
+
+ /**
+ * See [Math.subtractExact].
+ */
+ @JvmStatic
+ fun subtractExact(x: Long, y: Long): Long {
+ // copied from Math.java
+ val r = x - y
+ // HD 2-12 Overflow iff the arguments have different signs and
+ // the sign of the result is different than the sign of x
+ if (x xor y and (x xor r) < 0) {
+ throw ArithmeticException("long overflow")
+ }
+ return r
+ }
+
+ /**
+ * See [Math.multiplyExact].
+ */
+ @JvmStatic
+ fun multiplyExact(x: Int, y: Int): Int {
+ // copied from Math.java
+ val r = x.toLong() * y.toLong()
+ if (r.toInt().toLong() != r) {
+ throw ArithmeticException("integer overflow")
+ }
+ return r.toInt()
+ }
+
+ /**
+ * See [Math.multiplyExact].
+ */
+ @JvmStatic
+ fun multiplyExact(x: Long, y: Long): Long {
+ // copied from Math.java
+ val r = x * y
+ val ax = Math.abs(x)
+ val ay = Math.abs(y)
+ if (ax or ay ushr 31 != 0L) {
+ // Some bits greater than 2^31 that might cause overflow
+ // Check the result using the divide operator
+ // and check for the special case of Long.MIN_VALUE * -1
+ if ((y != 0L && r / y != x || x == Long.MIN_VALUE) && y == -1L) {
+ throw ArithmeticException("long overflow")
+ }
+ }
+ return r
+ }
+
+ /**
+ * See [Math.incrementExact].
+ */
+ @JvmStatic
+ fun incrementExact(a: Int): Int {
+ // copied from Math.java
+ if (a == Int.MAX_VALUE) {
+ throw ArithmeticException("integer overflow")
+ }
+ return a + 1
+ }
+
+ /**
+ * See [Math.incrementExact].
+ */
+ @JvmStatic
+ fun incrementExact(a: Long): Long {
+ // copied from Math.java
+ if (a == Long.MAX_VALUE) {
+ throw ArithmeticException("long overflow")
+ }
+ return a + 1L
+ }
+
+ /**
+ * See [Math.decrementExact].
+ */
+ @JvmStatic
+ fun decrementExact(a: Int): Int {
+ // copied from Math.java
+ if (a == Int.MIN_VALUE) {
+ throw ArithmeticException("integer overflow")
+ }
+ return a - 1
+ }
+
+ /**
+ * See [Math.decrementExact].
+ */
+ @JvmStatic
+ fun decrementExact(a: Long): Long {
+ // copied from Math.java
+ if (a == Long.MIN_VALUE) {
+ throw ArithmeticException("long overflow")
+ }
+ return a - 1L
+ }
+
+ /**
+ * See [Math.negateExact].
+ */
+ @JvmStatic
+ fun negateExact(a: Int): Int {
+ // copied from Math.java
+ if (a == Int.MIN_VALUE) {
+ throw ArithmeticException("integer overflow")
+ }
+ return -a
+ }
+
+ /**
+ * See [Math.negateExact].
+ */
+ @JvmStatic
+ fun negateExact(a: Long): Long {
+ // copied from Math.java
+ if (a == Long.MIN_VALUE) {
+ throw ArithmeticException("long overflow")
+ }
+ return -a
+ }
+
+ /**
+ * See [Math.toIntExact].
+ */
+ @JvmStatic
+ fun toIntExact(value: Long): Int {
+ // copied from Math.java
+ if (value.toInt().toLong() != value) {
+ throw ArithmeticException("integer overflow")
+ }
+ return value.toInt()
+ }
+
+ /**
+ * This method takes a numerical value and ensures it fits in a given numerical range. If the
+ * number is smaller than the minimum required by the range, then the minimum of the range will
+ * be returned. If the number is higher than the maximum allowed by the range then the maximum
+ * of the range will be returned.
+ *
+ * @param value the value to be clamped.
+ * @param min minimum resulting value.
+ * @param max maximum resulting value.
+ *
+ * @return the clamped value.
+ */
+ @JvmStatic
+ fun clamp(value: Float, min: Float, max: Float): Float {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+ }
+
+ /**
+ * This method takes a numerical value and ensures it fits in a given numerical range. If the
+ * number is smaller than the minimum required by the range, then the minimum of the range will
+ * be returned. If the number is higher than the maximum allowed by the range then the maximum
+ * of the range will be returned.
+ *
+ * @param value the value to be clamped.
+ * @param min minimum resulting value.
+ * @param max maximum resulting value.
+ *
+ * @return the clamped value.
+ */
+ @JvmStatic
+ fun clamp(value: Double, min: Double, max: Double): Double {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+ }
+
+ /**
+ * This method takes a numerical value and ensures it fits in a given numerical range. If the
+ * number is smaller than the minimum required by the range, then the minimum of the range will
+ * be returned. If the number is higher than the maximum allowed by the range then the maximum
+ * of the range will be returned.
+ *
+ * @param value the value to be clamped.
+ * @param min minimum resulting value.
+ * @param max maximum resulting value.
+ *
+ * @return the clamped value.
+ */
+ @JvmStatic
+ fun clamp(value: Int, min: Int, max: Int): Int {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+ }
+
+ /**
+ * This method takes a numerical value and ensures it fits in a given numerical range. If the
+ * number is smaller than the minimum required by the range, then the minimum of the range will
+ * be returned. If the number is higher than the maximum allowed by the range then the maximum
+ * of the range will be returned.
+ *
+ * @param value the value to be clamped.
+ * @param min minimum resulting value.
+ * @param max maximum resulting value.
+ *
+ * @return the clamped value.
+ */
+ @JvmStatic
+ fun clamp(value: Long, min: Long, max: Long): Long {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+ }
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/android/support/v4/util/LongSparseArray.kt b/tencent-map-compose/src/main/java/android/support/v4/util/LongSparseArray.kt
new file mode 100644
index 0000000..2a0ae88
--- /dev/null
+++ b/tencent-map-compose/src/main/java/android/support/v4/util/LongSparseArray.kt
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.util
+
+/**
+ * 处理:腾讯地图[com.tencent.tencentmap.mapsdk.vector.utils.clustering.algo.GridBasedAlgorithm]方法中需要用到support.v4的LongSparseArray
+ */
+@Suppress("unused")
+class LongSparseArray @JvmOverloads constructor(initialCapacity: Int = 10) : Cloneable {
+ private var mGarbage = false
+ private var mKeys: LongArray
+ private var mValues: Array
+ private var mSize: Int
+ /**
+ * Creates a new LongSparseArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ /**
+ * Creates a new LongSparseArray containing no mappings.
+ */
+ init {
+ var newInitialCapacity = initialCapacity
+ newInitialCapacity = idealLongArraySize(newInitialCapacity)
+ mKeys = LongArray(newInitialCapacity)
+ mValues = arrayOfNulls(newInitialCapacity)
+ mSize = 0
+ }
+
+ public override fun clone(): LongSparseArray {
+ var clone: LongSparseArray? = null
+ try {
+ clone = super.clone() as LongSparseArray
+ clone.mKeys = mKeys.clone()
+ clone.mValues = mValues.clone()
+ } catch (ignore: CloneNotSupportedException) {
+ }
+ return clone!!
+ }
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ /**
+ * Gets the Object mapped from the specified key, or `null`
+ * if no such mapping has been made.
+ */
+ @JvmOverloads
+ operator fun get(key: Long, valueIfKeyNotFound: E? = null): E? {
+ val i = binarySearch(mKeys, 0, mSize, key)
+ return if (i < 0 || mValues[i] === DELETED) {
+ valueIfKeyNotFound
+ } else {
+ mValues[i] as E?
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ fun delete(key: Long) {
+ val i = binarySearch(mKeys, 0, mSize, key)
+ if (i >= 0) {
+ if (mValues[i] !== DELETED) {
+ mValues[i] = DELETED
+ mGarbage = true
+ }
+ }
+ }
+
+ /**
+ * Alias for [.delete].
+ */
+ fun remove(key: Long) {
+ delete(key)
+ }
+
+ /**
+ * Removes the mapping at the specified index.
+ */
+ fun removeAt(index: Int) {
+ if (mValues[index] !== DELETED) {
+ mValues[index] = DELETED
+ mGarbage = true
+ }
+ }
+
+ private fun gc() {
+ // Log.e("SparseArray", "gc start with " + mSize);
+ val n = mSize
+ var o = 0
+ val keys = mKeys
+ val values = mValues
+ for (i in 0 until n) {
+ val `val` = values[i]
+ if (`val` !== DELETED) {
+ if (i != o) {
+ keys[o] = keys[i]
+ values[o] = `val`
+ values[i] = null
+ }
+ o++
+ }
+ }
+ mGarbage = false
+ mSize = o
+ // Log.e("SparseArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ fun put(key: Long, value: E) {
+ var i = binarySearch(mKeys, 0, mSize, key)
+ if (i >= 0) {
+ mValues[i] = value
+ } else {
+ i = i.inv()
+ if (i < mSize && mValues[i] === DELETED) {
+ mKeys[i] = key
+ mValues[i] = value
+ return
+ }
+ if (mGarbage && mSize >= mKeys.size) {
+ gc()
+ // Search again because indices may have changed.
+ i = binarySearch(mKeys, 0, mSize, key).inv()
+ }
+ if (mSize >= mKeys.size) {
+ val n = idealLongArraySize(mSize + 1)
+ val nkeys = LongArray(n)
+ val nvalues = arrayOfNulls(n)
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.size)
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.size)
+ mKeys = nkeys
+ mValues = nvalues
+ }
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i)
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i)
+ }
+ mKeys[i] = key
+ mValues[i] = value
+ mSize++
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this LongSparseArray
+ * currently stores.
+ */
+ fun size(): Int {
+ if (mGarbage) {
+ gc()
+ }
+ return mSize
+ }
+
+ /**
+ * Given an index in the range `0...size()-1`, returns
+ * the key from the `index`th key-value mapping that this
+ * LongSparseArray stores.
+ */
+ fun keyAt(index: Int): Long {
+ if (mGarbage) {
+ gc()
+ }
+ return mKeys[index]
+ }
+
+ /**
+ * Given an index in the range `0...size()-1`, returns
+ * the value from the `index`th key-value mapping that this
+ * LongSparseArray stores.
+ */
+ fun valueAt(index: Int): E? {
+ if (mGarbage) {
+ gc()
+ }
+ return mValues[index] as E?
+ }
+
+ /**
+ * Given an index in the range `0...size()-1`, sets a new
+ * value for the `index`th key-value mapping that this
+ * LongSparseArray stores.
+ */
+ fun setValueAt(index: Int, value: E) {
+ if (mGarbage) {
+ gc()
+ }
+ mValues[index] = value
+ }
+
+ /**
+ * Returns the index for which [.keyAt] would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ fun indexOfKey(key: Long): Int {
+ if (mGarbage) {
+ gc()
+ }
+ return binarySearch(mKeys, 0, mSize, key)
+ }
+
+ /**
+ * Returns an index for which [.valueAt] would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ fun indexOfValue(value: E): Int {
+ if (mGarbage) {
+ gc()
+ }
+ for (i in 0 until mSize) if (mValues[i] === value) return i
+ return -1
+ }
+
+ /**
+ * Removes all key-value mappings from this LongSparseArray.
+ */
+ fun clear() {
+ val n = mSize
+ val values = mValues
+ for (i in 0 until n) {
+ values[i] = null
+ }
+ mSize = 0
+ mGarbage = false
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ fun append(key: Long, value: E) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value)
+ return
+ }
+ if (mGarbage && mSize >= mKeys.size) {
+ gc()
+ }
+ val pos = mSize
+ if (pos >= mKeys.size) {
+ val n = idealLongArraySize(pos + 1)
+ val nkeys = LongArray(n)
+ val nvalues = arrayOfNulls(n)
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.size)
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.size)
+ mKeys = nkeys
+ mValues = nvalues
+ }
+ mKeys[pos] = key
+ mValues[pos] = value
+ mSize = pos + 1
+ }
+
+ companion object {
+ private val DELETED = Any()
+ private fun binarySearch(a: LongArray, start: Int, len: Int, key: Long): Int {
+ var high = start + len
+ var low = start - 1
+ var guess: Int
+ while (high - low > 1) {
+ guess = (high + low) / 2
+ if (a[guess] < key) low = guess else high = guess
+ }
+ return if (high == start + len) (start + len).inv() else if (a[high] == key) high else high.inv()
+ }
+
+ @JvmStatic
+ fun idealByteArraySize(need: Int): Int {
+ for (i in 4..31) if (need <= (1 shl i) - 12) return (1 shl i) - 12
+ return need
+ }
+
+ @JvmStatic
+ fun idealLongArraySize(need: Int): Int {
+ return idealByteArraySize(need * 8) / 8
+ }
+ }
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/android/support/v4/util/LruCache.kt b/tencent-map-compose/src/main/java/android/support/v4/util/LruCache.kt
new file mode 100644
index 0000000..4398061
--- /dev/null
+++ b/tencent-map-compose/src/main/java/android/support/v4/util/LruCache.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.util
+
+/**
+ * 处理:腾讯地图[com.tencent.tencentmap.mapsdk.vector.utils.clustering.algo.PreCachingAlgorithmDecorator]方法中需要用到support.v4的LruCache
+ */
+@Suppress("unused")
+class LruCache(maxSize: Int) {
+ private val map: LinkedHashMap
+
+ /** Size of this cache in units. Not necessarily the number of elements. */
+ private var size = 0
+ private val maxSize: Int
+ private var putCount = 0
+ private var createCount = 0
+ private var evictionCount = 0
+ private var hitCount = 0
+ private var missCount = 0
+
+ /**
+ * @param maxSize for caches that do not override [.sizeOf], this is
+ * the maximum number of entries in the cache. For all other caches,
+ * this is the maximum sum of the sizes of the entries in this cache.
+ */
+ init {
+ require(maxSize > 0) { "maxSize <= 0" }
+ this.maxSize = maxSize
+ map = LinkedHashMap(0, 0.75f, true)
+ }
+
+ /**
+ * Returns the value for `key` if it exists in the cache or can be
+ * created by `#create`. If a value was returned, it is moved to the
+ * head of the queue. This returns null if a value is not cached and cannot
+ * be created.
+ */
+ @Synchronized
+ operator fun get(key: K?): V? {
+ if (key == null) {
+ throw NullPointerException("key == null")
+ }
+ var result = map[key]
+ if (result != null) {
+ hitCount++
+ return result
+ }
+ missCount++
+ // TODO: release the lock while calling this potentially slow user code
+ result = create(key)
+ if (result != null) {
+ createCount++
+ size += safeSizeOf(key, result)
+ map[key] = result
+ trimToSize(maxSize)
+ }
+ return result
+ }
+
+ /**
+ * Caches `value` for `key`. The value is moved to the head of
+ * the queue.
+ *
+ * @return the previous value mapped by `key`. Although that entry is
+ * no longer cached, it has not been passed to [.entryEvicted].
+ */
+ @Synchronized
+ fun put(key: K?, value: V?): V? {
+ if (key == null || value == null) {
+ throw NullPointerException("key == null || value == null")
+ }
+ putCount++
+ size += safeSizeOf(key, value)
+ val previous = map.put(key, value)
+ if (previous != null) {
+ size -= safeSizeOf(key, previous)
+ }
+ trimToSize(maxSize)
+ return previous
+ }
+
+ private fun trimToSize(maxSize: Int) {
+ while (size > maxSize && !map.isEmpty()) {
+ val (key, value) = map.entries.iterator().next()
+ ?: break // map is empty; if size is not 0 then throw an error below
+ map.remove(key)
+ size -= safeSizeOf(key, value)
+ evictionCount++
+ // TODO: release the lock while calling this potentially slow user code
+ entryEvicted(key, value)
+ }
+ check(!(size < 0 || map.isEmpty() && size != 0)) {
+ (javaClass.name
+ + ".sizeOf() is reporting inconsistent results!")
+ }
+ }
+
+ /**
+ * Removes the entry for `key` if it exists.
+ *
+ * @return the previous value mapped by `key`. Although that entry is
+ * no longer cached, it has not been passed to [.entryEvicted].
+ */
+ @Synchronized
+ fun remove(key: K?): V? {
+ if (key == null) {
+ throw NullPointerException("key == null")
+ }
+ val previous = map.remove(key)
+ if (previous != null) {
+ size -= safeSizeOf(key, previous)
+ }
+ return previous
+ }
+
+ /**
+ * Called for entries that have reached the tail of the least recently used
+ * queue and are be removed. The default implementation does nothing.
+ */
+ protected fun entryEvicted(key: K, value: V) {}
+
+ /**
+ * Called after a cache miss to compute a value for the corresponding key.
+ * Returns the computed value or null if no value can be computed. The
+ * default implementation returns null.
+ */
+ protected fun create(key: K): V? {
+ return null
+ }
+
+ private fun safeSizeOf(key: K, value: V): Int {
+ val result = sizeOf(key, value)
+ check(result >= 0) { "Negative size: $key=$value" }
+ return result
+ }
+
+ /**
+ * Returns the size of the entry for `key` and `value` in
+ * user-defined units. The default implementation returns 1 so that size
+ * is the number of entries and max size is the maximum number of entries.
+ *
+ *
+ * An entry's size must not change while it is in the cache.
+ */
+ protected fun sizeOf(key: K, value: V): Int {
+ return 1
+ }
+
+ /**
+ * Clear the cache, calling [.entryEvicted] on each removed entry.
+ */
+ @Synchronized
+ fun evictAll() {
+ trimToSize(-1) // -1 will evict 0-sized elements
+ }
+
+ /**
+ * For caches that do not override [.sizeOf], this returns the number
+ * of entries in the cache. For all other caches, this returns the sum of
+ * the sizes of the entries in this cache.
+ */
+ @Synchronized
+ fun size(): Int {
+ return size
+ }
+
+ /**
+ * For caches that do not override [.sizeOf], this returns the maximum
+ * number of entries in the cache. For all other caches, this returns the
+ * maximum sum of the sizes of the entries in this cache.
+ */
+ @Synchronized
+ fun maxSize(): Int {
+ return maxSize
+ }
+
+ /**
+ * Returns the number of times [.get] returned a value.
+ */
+ @Synchronized
+ fun hitCount(): Int {
+ return hitCount
+ }
+
+ /**
+ * Returns the number of times [.get] returned null or required a new
+ * value to be created.
+ */
+ @Synchronized
+ fun missCount(): Int {
+ return missCount
+ }
+
+ /**
+ * Returns the number of times [.create] returned a value.
+ */
+ @Synchronized
+ fun createCount(): Int {
+ return createCount
+ }
+
+ /**
+ * Returns the number of times [.put] was called.
+ */
+ @Synchronized
+ fun putCount(): Int {
+ return putCount
+ }
+
+ /**
+ * Returns the number of values that have been evicted.
+ */
+ @Synchronized
+ fun evictionCount(): Int {
+ return evictionCount
+ }
+
+ /**
+ * Returns a copy of the current contents of the cache, ordered from least
+ * recently accessed to most recently accessed.
+ */
+ @Synchronized
+ fun snapshot(): Map {
+ return LinkedHashMap(map)
+ }
+
+ @Synchronized
+ override fun toString(): String {
+ val accesses = hitCount + missCount
+ val hitPercent = if (accesses != 0) 100 * hitCount / accesses else 0
+ return String.format(
+ "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
+ maxSize, hitCount, missCount, hitPercent
+ )
+ }
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapApplier.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapApplier.kt
index ce6b3ff..4b7b1f1 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapApplier.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapApplier.kt
@@ -22,7 +22,9 @@
package com.melody.map.tencent_compose
+import android.view.ViewGroup
import androidx.compose.runtime.*
+import androidx.compose.ui.platform.ComposeView
import com.melody.map.tencent_compose.adapter.ComposeInfoWindowAdapter
import com.melody.map.tencent_compose.overlay.DragState
import com.melody.map.tencent_compose.overlay.MarkerNode
@@ -51,12 +53,26 @@ internal class MapApplier(
private val decorations = mutableListOf()
+ /**
+ * 用于点聚合中,聚合点自定义InfoWindow样式的容器
+ */
+ internal val mClusterInfoWindowView: ComposeView
+ get() = ComposeView(mapView.context).apply {
+ mapView.addView(
+ this,
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ )
+ }
+
init {
attachClickListeners()
}
override fun onClear() {
- map.clear()
+ map.clearAllOverlays()
decorations.forEach { it.onCleared() }
decorations.clear()
}
@@ -84,9 +100,6 @@ internal class MapApplier(
private fun attachClickListeners() {
// 设置Marker的点击事件,return true拦截
map.setOnMarkerClickListener { marker ->
- // 优先处理普通Marker的事件,不匹配,再去查找轨迹移动的Marker
- /*decorations.nodeForMarker(marker)?.onMarkerClick?.invoke(marker)?:
- (decorations.nodeForMovingPointOverlay(marker)?.onMarkerClick?.invoke(marker)?: false)*/
decorations.nodeForMarker(marker)?.onMarkerClick?.invoke(marker)?:false
}
// Polyline的点击事件
@@ -103,15 +116,6 @@ internal class MapApplier(
override fun onInfoWindowClickLocation(p0: Int, p1: Int, p2: Int, p3: Int) {
}
})
- // MultiPointOverlay的点击事件
- /*map.setOnMultiPointClickListener { multiPointItem ->
- val node = decorations.nodeForMultiPoint(multiPointItem)
- if(null != node) {
- node.onPointItemClick.invoke(multiPointItem)
- return@setOnMultiPointClickListener true
- }
- return@setOnMultiPointClickListener false
- }*/
// 多边形点击事件
map.setOnPolygonClickListener { polygon, _ ->
decorations.nodeForPolygon(polygon)?.onClick?.invoke(polygon)
@@ -142,7 +146,7 @@ internal class MapApplier(
// 设置InfoWindow内容
map.setInfoWindowAdapter(
ComposeInfoWindowAdapter(
- mapView,
+ mapView = mapView,
markerNodeFinder = {
decorations.nodeForMarker(it)
}
@@ -156,12 +160,6 @@ internal class MapApplier(
private fun MutableList.nodeForMarker(marker: Marker): MarkerNode? =
fastFirstOrNull { it is MarkerNode && it.marker.options.title == marker.options.title && it.marker.options.snippet == marker.options.snippet } as? MarkerNode
-///**
-// * MovingPointOverlay轨迹移动
-// */
-//private fun MutableList.nodeForMovingPointOverlay(marker: Marker): MovingPointOverlayNode? =
-// fastFirstOrNull { it is MovingPointOverlayNode && it.marker.`object` == marker.`object` } as? MovingPointOverlayNode
-//
/**
* Polyline
*/
@@ -173,16 +171,3 @@ private fun MutableList.nodeForPolyline(polyline: Polyline): PolylineNo
*/
private fun MutableList.nodeForPolygon(polygon: Polygon): PolygonNode? =
fastFirstOrNull { it is PolygonNode && it.polygon == polygon } as? PolygonNode
-
-
-///**
-// * RoutePlanOverlay
-// */
-//private fun MutableList.nodeForRoutePlanPolyline(polyline: Polyline): RoutePlanOverlayNode? =
-// fastFirstOrNull { it is RoutePlanOverlayNode && null != it.routePlanOverlay?.allPolyLines?.fastFirstOrNull { child -> child == polyline } } as? RoutePlanOverlayNode
-//
-///**
-// * MultiPointOverlay
-// */
-//private fun MutableList.nodeForMultiPoint(multiPointItem: MultiPointItem): MultiPointOverlayNode? =
-// fastFirstOrNull { it is MultiPointOverlayNode && null != it.multiPointOverlay.items.fastFirstOrNull { child -> child == multiPointItem } } as? MultiPointOverlayNode
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapUpdater.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapUpdater.kt
index 5b1dd98..410988c 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapUpdater.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/MapUpdater.kt
@@ -36,6 +36,7 @@ import com.melody.map.tencent_compose.poperties.MapUiSettings
import com.melody.map.tencent_compose.position.CameraPositionState
import com.tencent.tencentmap.mapsdk.maps.LocationSource
import com.tencent.tencentmap.mapsdk.maps.TencentMap
+import com.tencent.tencentmap.mapsdk.maps.TencentMap.OnMapLoadedCallback
import com.tencent.tencentmap.mapsdk.maps.model.CameraPosition
import com.tencent.tencentmap.mapsdk.maps.model.RestrictBoundsFitMode
@@ -60,7 +61,14 @@ internal class MapPropertiesNode(
}
override fun onAttached() {
- map.setOnMapLoadedCallback { clickListeners.onMapLoaded() }
+ // 设置地图加载完成回调接口
+ map.addOnMapLoadedCallback(object :OnMapLoadedCallback{
+ override fun onMapLoaded() {
+ clickListeners.onMapLoaded()
+ // 移除监听
+ map.removeOnMapLoadedCallback(this)
+ }
+ })
map.setOnCameraChangeListener(object : TencentMap.OnCameraChangeListener {
override fun onCameraChange(cameraPosition: CameraPosition?) {
cameraPositionState.transformToTxCameraPosition(map.cameraPosition)
@@ -115,6 +123,22 @@ internal inline fun MapUpdater(
set(mapUiSettings.logoScale) { map.uiSettings.setLogoScale(it) }
// 设置定位监听
set(locationSource) { map.setLocationSource(it) }
+ // 设置地图是否允许多InfoWindow模式,默认是false(只允许显示一个InfoWindow)
+ set(mapProperties.enableMultipleInfoWindow) { map.enableMultipleInfowindow(it) }
+ // 是否显示地图标注及名称
+ set(mapProperties.isShowMapLabels) { map.setPoisEnabled(it) }
+ // 基于宽度限制地图显示范围
+ set(mapProperties.restrictWidthBounds) {
+ if(null != it) {
+ map.setRestrictBounds(it, RestrictBoundsFitMode.FIT_WIDTH)
+ }
+ }
+ // 基于高度显示地图范围
+ set(mapProperties.restrictHeightBounds) {
+ if(null != it) {
+ map.setRestrictBounds(it, RestrictBoundsFitMode.FIT_HEIGHT)
+ }
+ }
// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false
set(mapProperties.isMyLocationEnabled) { map.isMyLocationEnabled = it }
// 设置默认定位按钮是否显示,非必需设置。
@@ -125,6 +149,8 @@ internal inline fun MapUpdater(
set(mapProperties.isShowBuildings) { map.setBuilding3dEffectEnable(it) }
// 是否显示室内地图
set(mapProperties.isIndoorEnabled) { map.setIndoorEnabled(it) }
+ // 是否显示手绘图,**手绘图的主要应用场景是:景区**
+ set(mapProperties.isHandDrawMapEnable) { map.isHandDrawMapEnable = it }
// 是否显示路况图层
set(mapProperties.isTrafficEnabled) { map.isTrafficEnabled = it }
// 指南针控件是否可见
@@ -135,8 +161,8 @@ internal inline fun MapUpdater(
set(mapUiSettings.isTiltGesturesEnabled) { map.uiSettings.isTiltGesturesEnabled = it }
// 拖拽手势是否可用
set(mapUiSettings.isScrollGesturesEnabled) { map.uiSettings.isScrollGesturesEnabled = it }
- // 缩放按钮是否可见
- set(mapUiSettings.isZoomEnabled) { map.uiSettings.isZoomControlsEnabled = it }
+ // 缩放按钮是否可见,SDK从4.3.1弃用,Android对ZoomButton已不维护,建议使用自定义View
+ //set(mapUiSettings.isZoomEnabled) { map.uiSettings.isZoomControlsEnabled = it }
// 比例尺控件是否可见
set(mapUiSettings.isScaleControlsEnabled) { map.uiSettings.isScaleViewEnabled = it }
// 比例尺是否淡出
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/TXMap.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/TXMap.kt
index 8868e4a..80c5826 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/TXMap.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/TXMap.kt
@@ -39,7 +39,6 @@ import com.melody.map.tencent_compose.poperties.MapProperties
import com.melody.map.tencent_compose.poperties.MapUiSettings
import com.melody.map.tencent_compose.position.CameraPositionState
import com.melody.map.tencent_compose.position.rememberCameraPositionState
-import com.tencent.lbssearch.httpresponse.Poi
import com.tencent.tencentmap.mapsdk.maps.LocationSource
import com.tencent.tencentmap.mapsdk.maps.MapView
import com.tencent.tencentmap.mapsdk.maps.TencentMapOptions
@@ -47,6 +46,16 @@ import kotlinx.coroutines.awaitCancellation
/**
* 腾讯地图 TXMap
+ *
+ * @param modifier [Modifier]修饰符
+ * @param cameraPositionState 地图相机位置状态[CameraPositionState]
+ * @param tMapOptionsFactory 可以传腾讯地图[TencentMapOptions]参数,如离线地图开关等。
+ * @param properties 地图属性配置[MapProperties]
+ * @param uiSettings 地图SDK UI配置[MapUiSettings]
+ * @param locationSource 地图定位蓝点功能必传[LocationSource]
+ * @param onMapLoaded 地图加载完成的回调
+ * @param content 这里面放置-地图覆盖物
+ *
* @author 被风吹过的夏天
* @email developer_melody@163.com
* @github: https://github.com/TheMelody/OmniMap
@@ -56,13 +65,11 @@ import kotlinx.coroutines.awaitCancellation
fun TXMap(
modifier: Modifier = Modifier,
cameraPositionState: CameraPositionState = rememberCameraPositionState(),
- aMapOptionsFactory: () -> TencentMapOptions = { TencentMapOptions() },
+ tMapOptionsFactory: () -> TencentMapOptions = { TencentMapOptions() },
properties: MapProperties = DefaultMapProperties,
uiSettings: MapUiSettings = DefaultMapUiSettings,
locationSource: LocationSource? = null,
onMapLoaded: () -> Unit = {},
- onPOIClick: (Poi) -> Unit = {},
- //indoorBuildingActive: (IndoorBuildingInfo) -> Unit = {},
content: (@Composable @TXMapComposable () -> Unit)? = null
) {
if (LocalInspectionMode.current) {
@@ -70,7 +77,7 @@ fun TXMap(
}
val context = LocalContext.current
val mapView = remember {
- MapView(context, aMapOptionsFactory()).apply {
+ MapView(context, tMapOptionsFactory()).apply {
id = R.id.map
}
}
@@ -78,7 +85,6 @@ fun TXMap(
MapLifecycle(mapView)
val mapClickListeners = remember { MapClickListeners() }.also {
it.onMapLoaded = onMapLoaded
- it.onPOIClick = onPOIClick
}
val currentLocationSource by rememberUpdatedState(locationSource)
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/adapter/ClusterInfoWindowAdapter.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/adapter/ClusterInfoWindowAdapter.kt
new file mode 100644
index 0000000..b88c379
--- /dev/null
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/adapter/ClusterInfoWindowAdapter.kt
@@ -0,0 +1,92 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.map.tencent_compose.adapter
+
+import android.graphics.Color
+import android.view.View
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionContext
+import androidx.compose.ui.platform.ComposeView
+import com.tencent.tencentmap.mapsdk.maps.MapView
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.Cluster
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterItem
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterManager
+
+/**
+ * 多个聚合点以及单个聚合点Marker,被点击的时候,弹出来的InfoWindow
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 16:48
+ */
+internal class ClusterInfoWindowAdapter(
+ private val infoWindowView: ComposeView,
+ private val compositionContext: CompositionContext,
+ private val clusterInfoWindow: @Composable (Cluster?) -> Unit = {},
+ private val itemInfoWindow: @Composable (T) -> Unit = {}
+) : ClusterManager.ClusterInfoWindowAdapter, ClusterManager.ClusterItemInfoWindowAdapter {
+
+ private fun ComposeView.applyAndRemove(
+ parentContext: CompositionContext,
+ content: @Composable () -> Unit
+ ): ComposeView {
+ val result = this.apply {
+ setParentCompositionContext(parentContext)
+ setContent {
+ // 去除地图默认气泡背景, 如果是InfoContent,则只定制内容,不修改窗口背景和样式
+ setBackgroundColor(Color.TRANSPARENT)
+ content.invoke()
+ }
+ }
+ (this.parent as? MapView)?.removeView(this)
+ return result
+ }
+
+ override fun getInfoContents(cluster: Cluster?): View? {
+ return null
+ }
+
+ override fun getInfoWindow(cluster: Cluster?): View {
+ return infoWindowView.applyAndRemove(compositionContext) {
+ clusterInfoWindow(cluster)
+ }
+ }
+
+ override fun getInfoWindowPressState(cluster: Cluster?): View? {
+ return null
+ }
+
+ override fun getInfoContents(t: T): View? {
+ return null
+ }
+
+ override fun getInfoWindow(t: T): View {
+ return infoWindowView.applyAndRemove(compositionContext) {
+ itemInfoWindow(t)
+ }
+ }
+
+ override fun getInfoWindowPressState(t: T): View? {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/model/MapClickListeners.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/model/MapClickListeners.kt
index f88feb2..0e9209d 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/model/MapClickListeners.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/model/MapClickListeners.kt
@@ -25,10 +25,7 @@ package com.melody.map.tencent_compose.model
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import com.tencent.lbssearch.httpresponse.Poi
internal class MapClickListeners {
var onMapLoaded: () -> Unit by mutableStateOf({})
- var onPOIClick: (Poi) -> Unit by mutableStateOf({})
-// var indoorBuildingActive: (IndoorBuildingInfo) -> Unit by mutableStateOf({})
}
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/ClusterOverlay.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/ClusterOverlay.kt
new file mode 100644
index 0000000..3da5a82
--- /dev/null
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/ClusterOverlay.kt
@@ -0,0 +1,164 @@
+// MIT License
+//
+// Copyright (c) 2022 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.map.tencent_compose.overlay
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberCompositionContext
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import com.melody.map.tencent_compose.MapApplier
+import com.melody.map.tencent_compose.MapNode
+import com.melody.map.tencent_compose.adapter.ClusterInfoWindowAdapter
+import com.melody.map.tencent_compose.render.CustomClusterRenderer
+import com.melody.map.tencent_compose.model.TXMapComposable
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.Cluster
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterItem
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterManager
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.algo.Algorithm
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.view.DefaultClusterRenderer
+
+internal class ClusterOverlayNode(
+ val defaultClusterRenderer: DefaultClusterRenderer?,
+ val clusterManager: ClusterManager
+) : MapNode {
+ override fun onRemoved() {
+ clusterManager.cancel()
+ }
+}
+
+/**
+ * 点聚合效果ClusterOverlay
+ * @param minClusterSize 设置最小聚合数量,默认为4,这里设置为1,即有1个以上不包括1个marker才会聚合
+ * @param buckets 定义聚合的分段,默认配置:当超过5个不足10个的时候,显示5+,其他分段同理
+ * @param algorithm 默认为聚合策略[com.tencent.tencentmap.mapsdk.vector.utils.clustering.algo.NonHierarchicalDistanceBasedAlgorithm],调用时不必添加,如果需要其他聚合策略,可修改,可参考如下代码:
+```kt
+ // 默认聚合策略,调用时不必添加,如果需要其他聚合策略可以按以下代码修改
+ val ndba = NonHierarchicalDistanceBasedAlgorithm(context)
+ // 设置点聚合生效距离,以dp为单位
+ ndba.setMaxDistanceAtZoom(35)
+ // 设置策略
+ mClusterManager.setAlgorithm(ndba)
+```
+ * @param clusterItems 全部的聚合点
+ * @param clusterColor 聚合点圆的颜色,圆圈边缘半透明浅白色无法修改
+ * @param clusterItemIcon 单个Marker图标
+ * @param onClusterItemClick 设置在点击【单个聚合点Marker】项时调用的回调,传:**NULL**,**不会弹出InfoWindow**
+ * @param onClustersClick 设置点击【聚合点】时调用的回调,传:**NULL**,**不会弹出InfoWindow**
+ */
+@Composable
+@TXMapComposable
+fun ClusterOverlay(
+ minClusterSize: Int = 1,
+ buckets: IntArray = intArrayOf(5, 10, 20, 50),
+ algorithm: Algorithm? = null,
+ clusterItems: List,
+ clusterColor: Color? = null,
+ clusterItemIcon: BitmapDescriptor? = null,
+ onClusterItemClick: (ClusterItem?) -> Unit = {},
+ onClustersClick: (Cluster?) -> Unit = {},
+ onClusterItemInfoWindow: (@Composable (ClusterItem) -> Unit)? = null,
+ onClustersInfoWindow: (@Composable (Cluster?) -> Unit)? = null
+) {
+ val currentOnClustersClick by rememberUpdatedState(newValue = onClustersClick)
+ val currentOnClusterItemClick by rememberUpdatedState(newValue = onClusterItemClick)
+ val mapApplier = currentComposer.applier as? MapApplier
+ val context = LocalContext.current
+ val parentContext = rememberCompositionContext()
+ ComposeNode(
+ factory = {
+ val tMap = mapApplier?.map?: error("Error adding ClusterOverlay")
+ val clusterManager = ClusterManager(context,tMap)
+ val renderer = CustomClusterRenderer(
+ context = context,
+ tencentMap = tMap,
+ clusterColor = clusterColor,
+ clusterItemIcon = clusterItemIcon,
+ clusterManager = clusterManager
+ )
+ renderer.minClusterSize = minClusterSize
+ // 定义聚合的分段
+ renderer.buckets = buckets
+ // 设置策略
+ algorithm?.let { clusterManager.setAlgorithm(it) }
+ // 设置聚合渲染器
+ clusterManager.renderer = renderer
+ // 多聚合点,点击事件回调
+ clusterManager.setOnClusterClickListener { cluster ->
+ currentOnClustersClick.invoke(cluster)
+ false
+ }
+ // 单个聚合点Marker点击的事件回调
+ clusterManager.setOnClusterItemClickListener { item ->
+ currentOnClusterItemClick.invoke(item)
+ false
+ }
+ onClustersInfoWindow?.let {
+ // 多聚合点弹出来的InfoWindow
+ clusterManager.setClusterInfoWindowAdapter(
+ ClusterInfoWindowAdapter(
+ infoWindowView = mapApplier.mClusterInfoWindowView,
+ compositionContext = parentContext,
+ clusterInfoWindow = onClustersInfoWindow
+ )
+ )
+ }
+ onClusterItemInfoWindow?.let {
+ // 单个聚合点Marker弹出来的InfoWindow
+ clusterManager.setClusterItemInfoWindowAdapter(
+ ClusterInfoWindowAdapter(
+ infoWindowView = mapApplier.mClusterInfoWindowView,
+ compositionContext = parentContext,
+ itemInfoWindow = onClusterItemInfoWindow
+ )
+ )
+ }
+ // 添加聚合
+ tMap.setOnCameraChangeListener(clusterManager)
+ // 设置单个聚合点Marker的点击事件
+ tMap.setOnMarkerClickListener(clusterManager)
+
+ if(onClusterItemInfoWindow != null || onClustersInfoWindow != null) {
+ // 弹出的InfoWindow的点击事件
+ tMap.setOnInfoWindowClickListener(clusterManager)
+ // clusterManager托管InfoWindow
+ tMap.setInfoWindowAdapter(clusterManager)
+ }
+
+ ClusterOverlayNode(renderer, clusterManager)
+ },
+ update = {
+ set(minClusterSize) {
+ this.defaultClusterRenderer?.minClusterSize = it
+ }
+ set(clusterItems) {
+ this.clusterManager.clearItems()
+ this.clusterManager.addItems(it)
+ }
+ }
+ )
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Marker.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Marker.kt
index 75f8e1e..bf85822 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Marker.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Marker.kt
@@ -30,6 +30,7 @@ import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCompositionContext
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
@@ -37,6 +38,7 @@ import androidx.compose.ui.geometry.Offset
import com.melody.map.tencent_compose.MapApplier
import com.melody.map.tencent_compose.MapNode
import com.melody.map.tencent_compose.model.TXMapComposable
+import com.tencent.tencentmap.mapsdk.maps.model.AnimationListener
import com.tencent.tencentmap.mapsdk.maps.model.BaseAnimation
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor
import com.tencent.tencentmap.mapsdk.maps.model.LatLng
@@ -176,6 +178,8 @@ fun Marker(
zIndex: Float = 0.0f,
animation: BaseAnimation? = null,
onClick: (Marker) -> Boolean = { false },
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
onInfoWindowClick: (Marker) -> Unit = {},
) {
MarkerImpl(
@@ -194,8 +198,12 @@ fun Marker(
visible = visible,
zIndex = zIndex,
onClick = onClick,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
animation = animation,
onInfoWindowClick = onInfoWindowClick,
+ infoContent = null,
+ infoWindow = null
)
}
@@ -219,7 +227,7 @@ fun Marker(
* @param animation 标注动画
* @param onClick 标注点击事件回调
* @param onInfoWindowClick InfoWindow的点击事件回调
- * @param content 【可选】,用于自定义整个信息窗口。
+ * @param content 【可选】,用于自定义整个信息窗口,【里面动态的内容,建议通过title、snippet、tag的方式获取】
*/
@Composable
@TXMapComposable
@@ -240,6 +248,8 @@ fun MarkerInfoWindow(
zIndex: Float = 0.0f,
animation: BaseAnimation? = null,
onClick: (Marker) -> Boolean = { false },
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
onInfoWindowClick: (Marker) -> Unit = {},
content: (@Composable (Marker) -> Unit)? = null
) {
@@ -259,9 +269,12 @@ fun MarkerInfoWindow(
visible = visible,
zIndex = zIndex,
onClick = onClick,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
animation = animation,
onInfoWindowClick = onInfoWindowClick,
infoWindow = content,
+ infoContent = null,
)
}
@@ -285,7 +298,7 @@ fun MarkerInfoWindow(
* @param animation 标注动画
* @param onClick 标注点击事件回调
* @param onInfoWindowClick InfoWindow的点击事件回调
- * @param content (可选),用于自定义信息窗口的内容。
+ * @param content (可选),用于自定义信息窗口的内容,【里面动态的内容,建议通过title、snippet、tag的方式获取】
*/
@Composable
@TXMapComposable
@@ -306,6 +319,8 @@ fun MarkerInfoWindowContent(
zIndex: Float = 0.0f,
animation: BaseAnimation? = null,
onClick: (Marker) -> Boolean = { false },
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
onInfoWindowClick: (Marker) -> Unit = {},
content: (@Composable (Marker) -> Unit)? = null
) {
@@ -325,9 +340,12 @@ fun MarkerInfoWindowContent(
visible = visible,
zIndex = zIndex,
onClick = onClick,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
animation = animation,
onInfoWindowClick = onInfoWindowClick,
infoContent = content,
+ infoWindow = null
)
}
@@ -357,26 +375,30 @@ fun MarkerInfoWindowContent(
@Composable
@TXMapComposable
private fun MarkerImpl(
- state: MarkerState = rememberMarkerState(),
- alpha: Float = 1.0f,
- anchor: Offset = Offset(0.5f, 1.0f),
- draggable: Boolean = false,
- isClickable: Boolean = true,
- flat_stable: Boolean = false,
- clockwise_stable: Boolean = true,
- icon: BitmapDescriptor? = null,
- rotation: Float = 0.0f,
- tag: Any? = null,
- snippet: String? = null,
- title: String? = null,
- visible: Boolean = true,
- zIndex: Float = 0.0f,
- animation: BaseAnimation? = null,
- onClick: (Marker) -> Boolean = { false },
+ state: MarkerState,
+ alpha: Float,
+ anchor: Offset,
+ draggable: Boolean,
+ isClickable: Boolean,
+ flat_stable: Boolean,
+ clockwise_stable: Boolean,
+ icon: BitmapDescriptor?,
+ rotation: Float,
+ tag: Any?,
+ snippet: String?,
+ title: String?,
+ visible: Boolean,
+ zIndex: Float,
+ animation: BaseAnimation?,
+ onClick: (Marker) -> Boolean,
+ onAnimationStart: () -> Unit,
+ onAnimationEnd: () -> Unit,
onInfoWindowClick: (Marker) -> Unit = {},
- infoWindow: (@Composable (Marker) -> Unit)? = null,
- infoContent: (@Composable (Marker) -> Unit)? = null,
+ infoWindow: (@Composable (Marker) -> Unit)?,
+ infoContent: (@Composable (Marker) -> Unit)?,
) {
+ val currentOnAnimationStart by rememberUpdatedState(onAnimationStart)
+ val currentOnAnimationEnd by rememberUpdatedState(onAnimationEnd)
val mapApplier = currentComposer.applier as? MapApplier
val compositionContext = rememberCompositionContext()
ComposeNode(
@@ -399,9 +421,6 @@ private fun MarkerImpl(
) ?: error("Error adding marker")
marker.tag = tag
marker.isClickable = isClickable
- if(null != animation) {
- marker.startAnimation(animation)
- }
MarkerNode(
compositionContext = compositionContext,
marker = marker,
@@ -446,6 +465,14 @@ private fun MarkerImpl(
set(zIndex) { this.marker.setZIndex(it) }
set(animation) {
if(null != it) {
+ it.animationListener = object :AnimationListener{
+ override fun onAnimationStart() {
+ currentOnAnimationStart.invoke()
+ }
+ override fun onAnimationEnd() {
+ currentOnAnimationEnd.invoke()
+ }
+ }
marker.startAnimation(it)
} else {
marker.setAnimation(null)
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/MovingPointOverlay.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/MovingPointOverlay.kt
index 53d69af..29eaeb5 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/MovingPointOverlay.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/MovingPointOverlay.kt
@@ -53,7 +53,7 @@ internal class MovingPointOverlayNode(
* @param isStartSmoothMove 是否开始移动
* @param flat_stable【稳定的参数,初始化配置,不支持二次更新】Marker是否平贴在地图上
* @param clockwise_stable【稳定的参数,初始化配置,不支持二次更新】Marker旋转角度是否沿顺时针方向
- * @param totalDuration 2点之间移动的总时长
+ * @param totalDuration 移动的总时长
* @param anchor 移动的Marker的锚点位置
* @param alpha 移动的Marker透明度
* @param visible MovingPointOverlay的可见性
@@ -79,6 +79,9 @@ fun MovingPointOverlay(
ComposeNode(
factory = {
val aMap = mapApplier?.map?: error("Error init MovingPointOverlay")
+ if(points.isEmpty()){
+ error("Error points size == 0")
+ }
val marker = aMap.addMarker(MarkerOptions(points[0]).apply {
icon(icon)
zIndex(zIndex)
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Polyline.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Polyline.kt
index d557ea8..69eeb77 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Polyline.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/overlay/Polyline.kt
@@ -22,10 +22,11 @@
package com.melody.map.tencent_compose.overlay
-import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeNode
import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import com.melody.map.tencent_compose.MapApplier
@@ -33,11 +34,13 @@ import com.melody.map.tencent_compose.MapNode
import com.melody.map.tencent_compose.model.TXMapComposable
import com.tencent.tencentmap.mapsdk.maps.model.AlphaAnimation
import com.tencent.tencentmap.mapsdk.maps.model.Animation
+import com.tencent.tencentmap.mapsdk.maps.model.AnimationListener
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor
import com.tencent.tencentmap.mapsdk.maps.model.EmergeAnimation
import com.tencent.tencentmap.mapsdk.maps.model.LatLng
import com.tencent.tencentmap.mapsdk.maps.model.Polyline
import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions
+import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE
import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions.SegmentText
internal class PolylineNode(
@@ -60,6 +63,18 @@ class PolylineRainbow private constructor(
companion object {
/**
* 线的分段颜色(彩虹线)配置
+ *
+ * 【对应关系如下】:
+ * colors: listOf(color1,color2,color3,color4),indexes: listOf(0,1,2,3)
+ *
+ * 线上点【0, 1】之间颜色 => 取 color1
+ *
+ * 线上点【1, 2】之间颜色 => 取 color2
+ *
+ * 线上点【2, 3】之间颜色 => 取 color3
+ *
+ * 线上点 【3, 最后一个点】之间颜色 => 取 color4
+ *
* @param colors 每段索引之间的颜色,这个颜色同样支持纹理颜色
* @param indexes 分段线的顶点索引,这个索引值的数量必须和colors颜色列表数量相同
*/
@@ -117,18 +132,19 @@ class PolylineDynamicRoadName private constructor(
}
/**
- * 地图线段覆盖物。一个线段是多个连贯点的集合线段。
+ * 地图线段覆盖物。一个线段是多个连贯点的集合【普通线段】。
*
* 官方详细文档:https://lbs.qq.com/mobile/androidMapSDK/developerGuide/drawLines
*
* @param points 线段的坐标点列表
* @param appendPoints 在原有顶点上附加新的顶点
- * @param rainbow (可选),线的分段颜色(彩虹线)
- * @param customTexture_stable (可选,稳定参数,初始化配置,不支持二次更新),线上自定义的纹理,如:叠加纹理图
* @param dynamicRoadName (可选),线上动态路名,线段上添加文字标注,文字可以作为线的属性在线上绘制出来
- * @param polylineColor 线段的颜色
+ * @param polylineColor (可选,【不设置,则使用腾讯地图默认颜色】)线段的颜色
+ * @param polylineBorderColor (可选,【不设置,则使用腾讯地图默认颜色】)线段边框的颜色,需要修改borderWidth才能生效
* @param visible 线段的可见属性
- * @param lineType 线段的类型,必须是[PolylineOptions.LineType]里面的一种,如:PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE
+ * @param lineType 线段的类型,必须是[PolylineOptions.LineType]里面的一种,如:[PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE]
+ * @param pattern 设置虚线的样式,仅在:lineType = [PolylineOptions.LineType.LINE_TYPE_IMAGEINARYLINE]时才有效,pattern的元素数量必须是偶数个,每对元素分别表示虚线中实线区域的长度,以及空白区域的长度(单位px),
+ * 【注意】:设置虚线,还需要设置[polylineColor]和[polylineBorderColor]属性值,否则:无法覆盖默认的虚线颜色,或者无法显示虚线
* @param useGradient 线段是否使用渐变色
* @param isRoad 线段是否为路线
* @param isLineCap 路线是否显示半圆端点
@@ -136,7 +152,10 @@ class PolylineDynamicRoadName private constructor(
* @param animation 动画,目前仅支持[AlphaAnimation]或者[EmergeAnimation]
* @param tag 线段的附件对象
* @param width 线段宽度
+ * @param borderWidth 线段边框的宽度,默认为0
* @param zIndex 显示层级
+ * @param onAnimationStart 线段动画开始的回调
+ * @param onAnimationEnd 线段动画完成的回调
* @param onClick polyline点击事件回调
*/
@Composable
@@ -144,25 +163,258 @@ class PolylineDynamicRoadName private constructor(
fun Polyline(
points: List,
appendPoints: List = emptyList(),
- rainbow: PolylineRainbow? = null,
- customTexture_stable: PolylineCustomTexture? = null,
dynamicRoadName: PolylineDynamicRoadName? = null,
- polylineColor: Color = Color.Black,
+ polylineColor: Color? = null,
+ polylineBorderColor: Color? = null,
visible: Boolean = true,
useGradient: Boolean = false,
isRoad: Boolean = true,
isLineCap: Boolean = false,
isClickable: Boolean = true,
animation: Animation? = null,
- lineType : Int? = null,
+ lineType: Int = LINE_TYPE_MULTICOLORLINE,
+ pattern: List = listOf(),
+ tag: Any? = null,
+ width: Float = 10F,
+ borderWidth: Float = 0F,
+ zIndex: Float = 0F,
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
+ onClick: (Polyline) -> Unit = {}
+) {
+ PolylineImpl(
+ points = points,
+ appendPoints = appendPoints,
+ rainbow = null,
+ customTexture_stable = null,
+ dynamicRoadName = dynamicRoadName,
+ polylineColor = polylineColor,
+ polylineBorderColor = polylineBorderColor,
+ visible = visible,
+ useGradient = useGradient,
+ isRoad = isRoad,
+ isLineCap = isLineCap,
+ isClickable = isClickable,
+ animation = animation,
+ lineType = lineType,
+ pattern = pattern,
+ tag = tag,
+ width = width,
+ borderWidth = borderWidth,
+ zIndex = zIndex,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
+ onClick = onClick
+ )
+}
+
+
+/**
+ * 地图线段覆盖物。一个线段是多个连贯点的集合【彩虹线段】
+ *
+ * 官方详细文档:https://lbs.qq.com/mobile/androidMapSDK/developerGuide/drawLines
+ *
+ * @param points 线段的坐标点列表
+ * @param appendPoints 在原有顶点上附加新的顶点
+ * @param rainbow 线的分段颜色(彩虹线)
+ * @param dynamicRoadName (可选),线上动态路名,线段上添加文字标注,文字可以作为线的属性在线上绘制出来
+ * @param visible 线段的可见属性
+ * @param lineType 线段的类型,必须是[PolylineOptions.LineType]里面的一种,如:[PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE]
+ * @param isRoad 线段是否为路线
+ * @param useGradient 线段是否为渐变的彩虹线段,如果设置为false,颜色一块是一块,如果设置为true,线段是多个颜色渐变连贯的
+ * @param isLineCap 路线是否显示半圆端点
+ * @param isClickable 是否可点击
+ * @param animation 动画,目前仅支持[AlphaAnimation]或者[EmergeAnimation]
+ * @param tag 线段的附件对象
+ * @param width 线段宽度
+ * @param borderWidth 线段边框的宽度,默认为0
+ * @param zIndex 显示层级
+ * @param onAnimationStart 线段动画开始的回调
+ * @param onAnimationEnd 线段动画完成的回调
+ * @param onClick polyline点击事件回调
+ */
+@Composable
+@TXMapComposable
+fun PolylineRainbow(
+ points: List,
+ appendPoints: List = emptyList(),
+ rainbow: PolylineRainbow?,
+ useGradient: Boolean,
+ dynamicRoadName: PolylineDynamicRoadName? = null,
+ visible: Boolean = true,
+ isRoad: Boolean = true,
+ isLineCap: Boolean = false,
+ isClickable: Boolean = true,
+ animation: Animation? = null,
+ lineType: Int = LINE_TYPE_MULTICOLORLINE,
+ tag: Any? = null,
+ width: Float = 10F,
+ borderWidth: Float = 0F,
+ zIndex: Float = 0F,
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
+ onClick: (Polyline) -> Unit = {}
+) {
+ PolylineImpl(
+ points = points,
+ appendPoints = appendPoints,
+ rainbow = rainbow,
+ customTexture_stable = null,
+ dynamicRoadName = dynamicRoadName,
+ polylineColor = null,
+ polylineBorderColor = null,
+ visible = visible,
+ useGradient = useGradient,
+ isRoad = isRoad,
+ isLineCap = isLineCap,
+ isClickable = isClickable,
+ animation = animation,
+ lineType = lineType,
+ pattern = null,
+ tag = tag,
+ width = width,
+ borderWidth = borderWidth,
+ zIndex = zIndex,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
+ onClick = onClick
+ )
+}
+
+/**
+ * 地图线段覆盖物。一个线段是多个连贯点的集合【纹理线段】
+ *
+ * 官方详细文档:https://lbs.qq.com/mobile/androidMapSDK/developerGuide/drawLines
+ *
+ * @param points 线段的坐标点列表
+ * @param appendPoints 在原有顶点上附加新的顶点
+ * @param customTexture_stable (初始化配置,不支持二次更新),线上自定义的纹理,如:叠加纹理图
+ * @param dynamicRoadName (可选),线上动态路名,线段上添加文字标注,文字可以作为线的属性在线上绘制出来
+ * @param polylineColor (可选,【不设置,则使用腾讯地图默认颜色】)线段的颜色
+ * @param polylineBorderColor (可选,【不设置,则使用腾讯地图默认颜色】)线段边框的颜色,需要修改borderWidth才能生效
+ * @param visible 线段的可见属性
+ * @param lineType 线段的类型,必须是[PolylineOptions.LineType]里面的一种,如:[PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE]
+ * @param isRoad 线段是否为路线
+ * @param isLineCap 路线是否显示半圆端点
+ * @param isClickable 是否可点击
+ * @param animation 动画,目前仅支持[AlphaAnimation]或者[EmergeAnimation]
+ * @param tag 线段的附件对象
+ * @param width 线段宽度
+ * @param borderWidth 线段边框的宽度,默认为0
+ * @param zIndex 显示层级
+ * @param onAnimationStart 线段动画开始的回调
+ * @param onAnimationEnd 线段动画完成的回调
+ * @param onClick polyline点击事件回调
+ */
+@Composable
+@TXMapComposable
+fun PolylineCustomTexture(
+ points: List,
+ appendPoints: List = emptyList(),
+ customTexture_stable: PolylineCustomTexture?,
+ dynamicRoadName: PolylineDynamicRoadName? = null,
+ polylineColor: Color? = null,
+ polylineBorderColor: Color? = null,
+ visible: Boolean = true,
+ isRoad: Boolean = true,
+ isLineCap: Boolean = false,
+ isClickable: Boolean = true,
+ animation: Animation? = null,
+ lineType: Int = LINE_TYPE_MULTICOLORLINE,
tag: Any? = null,
- width: Float = 10f,
- zIndex: Float = 0f,
+ width: Float = 10F,
+ borderWidth: Float = 0F,
+ zIndex: Float = 0F,
+ onAnimationStart: () -> Unit = {},
+ onAnimationEnd: () -> Unit = {},
onClick: (Polyline) -> Unit = {}
+) {
+ PolylineImpl(
+ points = points,
+ appendPoints = appendPoints,
+ rainbow = null,
+ customTexture_stable = customTexture_stable,
+ dynamicRoadName = dynamicRoadName,
+ polylineColor = polylineColor,
+ polylineBorderColor = polylineBorderColor,
+ visible = visible,
+ useGradient = true,
+ isRoad = isRoad,
+ isLineCap = isLineCap,
+ isClickable = isClickable,
+ animation = animation,
+ lineType = lineType,
+ pattern = null,
+ tag = tag,
+ width = width,
+ borderWidth = borderWidth,
+ zIndex = zIndex,
+ onAnimationStart = onAnimationStart,
+ onAnimationEnd = onAnimationEnd,
+ onClick = onClick
+ )
+}
+
+/**
+ * 【Polyline实现类】地图线段覆盖物。一个线段是多个连贯点的集合线段。
+ *
+ * 官方详细文档:https://lbs.qq.com/mobile/androidMapSDK/developerGuide/drawLines
+ *
+ * @param points 线段的坐标点列表
+ * @param appendPoints 在原有顶点上附加新的顶点
+ * @param rainbow (可选),线的分段颜色(彩虹线)
+ * @param customTexture_stable (可选,稳定参数,初始化配置,不支持二次更新),线上自定义的纹理,如:叠加纹理图
+ * @param dynamicRoadName (可选),线上动态路名,线段上添加文字标注,文字可以作为线的属性在线上绘制出来
+ * @param polylineColor (可选,【不设置,则使用腾讯地图默认颜色】)线段的颜色
+ * @param polylineBorderColor (可选,【不设置,则使用腾讯地图默认颜色】)线段边框的颜色,需要修改borderWidth才能生效
+ * @param visible 线段的可见属性
+ * @param lineType 线段的类型,必须是[PolylineOptions.LineType]里面的一种,如:[PolylineOptions.LineType.LINE_TYPE_MULTICOLORLINE]
+ * @param pattern 设置虚线的样式,仅在:lineType = [PolylineOptions.LineType.LINE_TYPE_IMAGEINARYLINE]时才有效,pattern的元素数量必须是偶数个,每对元素分别表示虚线中实线区域的长度,以及空白区域的长度(单位px),
+ * 【注意】:设置虚线,还需要设置[polylineColor]和[polylineBorderColor]属性值,否则:无法覆盖默认的虚线颜色,或者无法显示虚线
+ * @param useGradient 线段是否为渐变的彩虹线段【默认为true】,如果设置为false,颜色一块是一块,如果设置为true,线段是多个颜色渐变连贯的
+ * @param isRoad 线段是否为路线
+ * @param isLineCap 路线是否显示半圆端点
+ * @param isClickable 是否可点击
+ * @param animation 动画,目前仅支持[AlphaAnimation]或者[EmergeAnimation]
+ * @param tag 线段的附件对象
+ * @param width 线段宽度
+ * @param borderWidth 线段边框的宽度,默认为0
+ * @param zIndex 显示层级
+ * @param onAnimationStart 线段动画开始的回调
+ * @param onAnimationEnd 线段动画完成的回调
+ * @param onClick polyline点击事件回调
+ */
+@Composable
+@TXMapComposable
+private fun PolylineImpl(
+ points: List,
+ appendPoints: List,
+ rainbow: PolylineRainbow?,
+ customTexture_stable: PolylineCustomTexture?,
+ dynamicRoadName: PolylineDynamicRoadName?,
+ polylineColor: Color?,
+ polylineBorderColor: Color?,
+ visible: Boolean,
+ useGradient: Boolean,
+ isRoad: Boolean,
+ isLineCap: Boolean,
+ isClickable: Boolean,
+ animation: Animation?,
+ lineType: Int,
+ pattern: List?,
+ tag: Any?,
+ width: Float,
+ borderWidth: Float,
+ zIndex: Float,
+ onAnimationStart: () -> Unit,
+ onAnimationEnd: () -> Unit,
+ onClick: (Polyline) -> Unit
) {
if(null != animation && !(animation is AlphaAnimation || animation is EmergeAnimation)) {
error("animation must be either AlphaAnimation or EmergeAnimation")
}
+ val currentOnAnimationStart by rememberUpdatedState(onAnimationStart)
+ val currentOnAnimationEnd by rememberUpdatedState(onAnimationEnd)
val mapApplier = currentComposer.applier as MapApplier?
ComposeNode(
factory = {
@@ -170,29 +422,49 @@ fun Polyline(
PolylineOptions().apply {
addAll(points)
lineCap(isLineCap)
- color(polylineColor.toArgb())
- lineType?.let { lineType(it) }
+ polylineColor?.let { color(polylineColor.toArgb()) }
+ if(borderWidth > 0 && null != polylineBorderColor){
+ borderColors(intArrayOf(polylineBorderColor.toArgb()))
+ }
gradient(useGradient)
- road(isRoad)
+ if(useGradient) {
+ // 这里规避下,如果外部设置为true,则必须设置下面这个类型,且road也必须为true
+ lineType(LINE_TYPE_MULTICOLORLINE)
+ road(true)
+ } else {
+ lineType(lineType)
+ road(isRoad)
+ }
+ if(pattern?.isNotEmpty() == true) {
+ // 线段虚线样式
+ pattern(pattern)
+ }
clickable(isClickable)
visible(visible)
width(width)
+ borderWidth(borderWidth)
customTexture(customTexture_stable)
}) ?: error("Error adding Polyline")
polyline.tag = tag
polyline.rainbowColorLine(rainbow)
polyline.dynamicRoadName(dynamicRoadName)
- if(null != animation) {
- polyline.startAnimation(animation)
- }
PolylineNode(polyline, onClick)
},
update = {
update(onClick) { this.onPolylineClick = it }
set(points) { this.polyline.points = it }
- set(appendPoints) { this.polyline.appendPoints(it) }
- set(polylineColor) { this.polyline.color = it.toArgb() }
+ set(appendPoints) {
+ if(it.isNotEmpty()) {
+ this.polyline.appendPoints(it)
+ }
+ }
+ set(polylineColor) { it?.let { this.polyline.color = it.toArgb() } }
+ set(polylineBorderColor) {
+ if(borderWidth > 0 && null != polylineBorderColor){
+ this.polyline.setBorderColors(intArrayOf(polylineBorderColor.toArgb()))
+ }
+ }
set(tag) { this.polyline.tag = it }
set(rainbow) { this.polyline.rainbowColorLine(it) }
set(useGradient) { this.polyline.isGradientEnable = it }
@@ -201,6 +473,14 @@ fun Polyline(
set(isClickable) { this.polyline.isClickable = it }
set(animation) {
if(null != it) {
+ it.animationListener = object : AnimationListener{
+ override fun onAnimationStart() {
+ currentOnAnimationStart.invoke()
+ }
+ override fun onAnimationEnd() {
+ currentOnAnimationEnd.invoke()
+ }
+ }
this.polyline.startAnimation(it)
} else {
this.polyline.setAnimation(null)
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapProperties.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapProperties.kt
index 890c493..da495f9 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapProperties.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapProperties.kt
@@ -30,12 +30,17 @@ import java.util.Objects
val DefaultMapProperties = MapProperties()
/**
+ * MapProperties
* @param isShowBuildings 是否显示3D楼块效果
* @param isIndoorEnabled 是否显示室内地图,目前室内图这玩意,权限申请【没有开通在线申请】,如需要请联系室内图商务协助办理:
* https://lbs.qq.com/mobile/androidMapSDK/developerGuide/indoor
+ * @param isShowMapLabels 是否显示地图标注及名称
* @param enableMultipleInfoWindow 多窗口模式默认是关闭的,是否启用可以在地图上显示多个Marker覆盖物上方的信息窗口
+ * @param restrictWidthBounds 基于宽度限制地图显示范围: tencentMap.setRestrictBounds(latLngBounds, RestrictBoundsFitMode.FIT_WIDTH)
+ * @param restrictHeightBounds 基于高度显示地图范围: tencentMap.setRestrictBounds(latLngBounds, RestrictBoundsFitMode.FIT_HEIGHT)
* @param isMyLocationEnabled 设置是否打开定位图层(myLocationOverlay)
* @param isTrafficEnabled 是否打开交通路况图层
+ * @param isHandDrawMapEnable 是否显示手绘图,**手绘图的主要应用场景是:景区**
* @param myLocationStyle 设置定位图层(myLocationOverlay)的样式
* @param maxZoomPreference 设置地图最大缩放级别 缩放级别范围为[3, 20],超出范围将按最大级别计算
* @param minZoomPreference 设置最小缩放级别 缩放级别范围为[3, 20],超出范围将按最小级别计算
@@ -45,9 +50,13 @@ val DefaultMapProperties = MapProperties()
class MapProperties(
val isShowBuildings: Boolean = false,
val isIndoorEnabled: Boolean = false,
+ val isShowMapLabels: Boolean = true,
val enableMultipleInfoWindow: Boolean = false,
+ val restrictWidthBounds: LatLngBounds? = null,
+ val restrictHeightBounds: LatLngBounds? = null,
val isMyLocationEnabled: Boolean = false,
val isTrafficEnabled: Boolean = false,
+ val isHandDrawMapEnable: Boolean = false,
val myLocationStyle: MyLocationStyle? = null,
val maxZoomPreference: Float = 21.0F,
val minZoomPreference: Float = 0F,
@@ -58,9 +67,13 @@ class MapProperties(
override fun equals(other: Any?): Boolean = other is MapProperties &&
isShowBuildings == other.isShowBuildings &&
isIndoorEnabled == other.isIndoorEnabled &&
+ isShowMapLabels == other.isShowMapLabels &&
enableMultipleInfoWindow == other.enableMultipleInfoWindow &&
+ restrictWidthBounds == other.restrictWidthBounds &&
+ restrictHeightBounds == other.restrictHeightBounds &&
isMyLocationEnabled == other.isMyLocationEnabled &&
isTrafficEnabled == other.isTrafficEnabled &&
+ isHandDrawMapEnable == other.isHandDrawMapEnable &&
myLocationStyle == other.myLocationStyle &&
maxZoomPreference == other.maxZoomPreference &&
minZoomPreference == other.minZoomPreference &&
@@ -70,9 +83,13 @@ class MapProperties(
override fun hashCode(): Int = Objects.hash(
isShowBuildings,
isIndoorEnabled,
+ isShowMapLabels,
enableMultipleInfoWindow,
+ restrictWidthBounds,
+ restrictHeightBounds,
isMyLocationEnabled,
isTrafficEnabled,
+ isHandDrawMapEnable,
myLocationStyle,
maxZoomPreference,
minZoomPreference,
@@ -83,9 +100,13 @@ class MapProperties(
fun copy(
isShowBuildings: Boolean = this.isShowBuildings,
isIndoorEnabled: Boolean = this.isIndoorEnabled,
+ isShowMapLabels: Boolean = this.isShowMapLabels,
enableMultipleInfoWindow: Boolean = this.enableMultipleInfoWindow,
+ restrictWidthBounds: LatLngBounds? = this.restrictWidthBounds,
+ restrictHeightBounds: LatLngBounds? = this.restrictHeightBounds,
isMyLocationEnabled: Boolean = this.isMyLocationEnabled,
isTrafficEnabled: Boolean = this.isTrafficEnabled,
+ isHandDrawMapEnable: Boolean = this.isHandDrawMapEnable,
myLocationStyle: MyLocationStyle? = this.myLocationStyle,
maxZoomPreference: Float = this.maxZoomPreference,
minZoomPreference: Float = this.minZoomPreference,
@@ -94,9 +115,13 @@ class MapProperties(
): MapProperties = MapProperties(
isShowBuildings = isShowBuildings,
isIndoorEnabled = isIndoorEnabled,
+ isShowMapLabels = isShowMapLabels,
enableMultipleInfoWindow = enableMultipleInfoWindow,
+ restrictWidthBounds = restrictWidthBounds,
+ restrictHeightBounds = restrictHeightBounds,
isMyLocationEnabled = isMyLocationEnabled,
isTrafficEnabled = isTrafficEnabled,
+ isHandDrawMapEnable = isHandDrawMapEnable,
myLocationStyle = myLocationStyle,
maxZoomPreference = maxZoomPreference,
minZoomPreference = minZoomPreference,
@@ -107,9 +132,13 @@ class MapProperties(
override fun toString(): String {
return "MapProperties(isShowBuildings=$isShowBuildings, " +
"isIndoorEnabled=$isIndoorEnabled, " +
+ "isShowMapLabels=$isShowMapLabels, " +
"enableMultipleInfoWindow=$enableMultipleInfoWindow, " +
+ "restrictWidthBounds=$restrictWidthBounds, " +
+ "restrictHeightBounds=$restrictHeightBounds, " +
"isMyLocationEnabled=$isMyLocationEnabled, " +
"isTrafficEnabled=$isTrafficEnabled, " +
+ "isHandDrawMapEnable=$isHandDrawMapEnable, " +
"myLocationStyle=$myLocationStyle, " +
"maxZoomPreference=$maxZoomPreference, " +
"minZoomPreference=$minZoomPreference, " +
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapUiSettings.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapUiSettings.kt
index 4137420..d5a13ae 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapUiSettings.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/poperties/MapUiSettings.kt
@@ -29,12 +29,12 @@ import java.util.*
internal val DefaultMapUiSettings = MapUiSettings()
/**
+ * MapUiSettings
* @param logoScale 地图Logo的缩放比例,比例范围(0.7~1.3)
* @param isRotateGesturesEnabled 旋转手势是否可用
* @param isScrollGesturesEnabled 拖拽手势是否可用
* @param isTiltGesturesEnabled 倾斜手势是否可用
* @param isZoomGesturesEnabled 缩放手势是否可用
- * @param isZoomEnabled 缩放按钮是否可见
* @param isCompassEnabled 指南针控件是否可见
* @param myLocationButtonEnabled 设置默认定位按钮是否显示,非必需设置。
* @param isScaleControlsEnabled 比例尺控件是否可见
@@ -48,7 +48,6 @@ class MapUiSettings(
val isScrollGesturesEnabled: Boolean = false,
val isTiltGesturesEnabled: Boolean = false,
val isZoomGesturesEnabled: Boolean = false,
- val isZoomEnabled: Boolean = false,
val isCompassEnabled: Boolean = false,
val myLocationButtonEnabled: Boolean = false,
val isScaleControlsEnabled: Boolean = false,
@@ -63,7 +62,6 @@ class MapUiSettings(
isScrollGesturesEnabled == other.isScrollGesturesEnabled &&
isTiltGesturesEnabled == other.isTiltGesturesEnabled &&
isZoomGesturesEnabled == other.isZoomGesturesEnabled &&
- isZoomEnabled == other.isZoomEnabled &&
isCompassEnabled == other.isCompassEnabled &&
myLocationButtonEnabled == other.myLocationButtonEnabled &&
isScaleControlsEnabled == other.isScaleControlsEnabled &&
@@ -77,7 +75,6 @@ class MapUiSettings(
isScrollGesturesEnabled,
isTiltGesturesEnabled,
isZoomGesturesEnabled,
- isZoomEnabled,
isCompassEnabled,
myLocationButtonEnabled,
isScaleControlsEnabled,
@@ -92,7 +89,6 @@ class MapUiSettings(
isScrollGesturesEnabled: Boolean = this.isScrollGesturesEnabled,
isTiltGesturesEnabled: Boolean = this.isTiltGesturesEnabled,
isZoomGesturesEnabled: Boolean = this.isZoomGesturesEnabled,
- isZoomEnabled: Boolean = this.isZoomEnabled,
isCompassEnabled: Boolean = this.isCompassEnabled,
myLocationButtonEnabled: Boolean = this.myLocationButtonEnabled,
isScaleControlsEnabled: Boolean = this.isScaleControlsEnabled,
@@ -105,7 +101,6 @@ class MapUiSettings(
isScrollGesturesEnabled = isScrollGesturesEnabled,
isTiltGesturesEnabled = isTiltGesturesEnabled,
isZoomGesturesEnabled = isZoomGesturesEnabled,
- isZoomEnabled = isZoomEnabled,
isCompassEnabled = isCompassEnabled,
myLocationButtonEnabled = myLocationButtonEnabled,
isScaleControlsEnabled = isScaleControlsEnabled,
@@ -121,7 +116,6 @@ class MapUiSettings(
"isScrollGesturesEnabled=$isScrollGesturesEnabled," +
"isTiltGesturesEnabled=$isTiltGesturesEnabled, " +
"isZoomGesturesEnabled=$isZoomGesturesEnabled, " +
- "isZoomEnabled=$isZoomEnabled, " +
"isCompassEnabled=$isCompassEnabled, " +
"myLocationButtonEnabled=$myLocationButtonEnabled, " +
"isScaleViewFadeEnable=$isScaleViewFadeEnable, " +
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/position/CameraPositionState.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/position/CameraPositionState.kt
index c2edcbb..064105a 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/position/CameraPositionState.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/position/CameraPositionState.kt
@@ -76,7 +76,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
* coordinates and lat/lng.
*/
val projection: Projection?
- get() = map?.projection
+ get() = tMap?.projection
/**
* Local source of truth for the current camera position.
@@ -87,7 +87,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
private var rawPosition by mutableStateOf(position)
/**
- * 注意:这里要转换一下CameraPosition,高德地图实现了Parcelable,腾讯地图没有实现,我们这里转换一下,才能用rememberSaveable
+ * 注意:这里要转换一下CameraPosition,腾讯地图没有实现Parcelable,我们这里转换一下,才能用rememberSaveable
*/
fun transformToTxCameraPosition(cameraPosition: CameraPosition) {
rawPosition = TXCameraPosition(cameraPosition.target,cameraPosition.zoom,cameraPosition.tilt,cameraPosition.bearing)
@@ -97,7 +97,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
get() = rawPosition
set(value) {
synchronized(lock) {
- val map = map
+ val map = tMap
if (map == null) {
rawPosition = value
} else {
@@ -106,7 +106,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
}
}
- private var map: TencentMap? = null
+ private var tMap: TencentMap? = null
private val lock = Any()
@@ -141,11 +141,11 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
internal fun setMap(map: TencentMap?) {
synchronized(lock) {
- if (this.map == null && map == null) return
- if (this.map != null && map != null) {
+ if (this.tMap == null && map == null) return
+ if (this.tMap != null && map != null) {
error("CameraPositionState may only be associated with one TencentMap at a time")
}
- this.map = map
+ this.tMap = map
if (map == null) {
isMoving = false
} else {
@@ -189,14 +189,13 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
suspendCancellableCoroutine { continuation ->
synchronized(lock) {
movementOwner = myJob
- val map = map
+ val map = tMap
if (map == null) {
// Do it later
val animateOnMapAvailable = object : OnMapChangedCallback {
override fun onMapChangedLocked(newMap: TencentMap?) {
if (newMap == null) {
// Cancel the animate caller and crash the map setter
- @Suppress("ThrowableNotThrown")
continuation.resumeWithException(CancellationException(
"internal error; no TencentMap available"))
error(
@@ -234,7 +233,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
synchronized(lock) {
if (myJob != null && movementOwner === myJob) {
movementOwner = null
- map?.stopAnimation()
+ tMap?.stopAnimation()
}
}
}
@@ -280,7 +279,7 @@ class CameraPositionState(position: TXCameraPosition = TXCameraPosition(LatLng(3
@UiThread
fun move(update: CameraUpdate) {
synchronized(lock) {
- val map = map
+ val map = tMap
movementOwner = null
if (map == null) {
// Do it when we have a map available
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/render/CustomClusterRenderer.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/render/CustomClusterRenderer.kt
new file mode 100644
index 0000000..5f6b02e
--- /dev/null
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/render/CustomClusterRenderer.kt
@@ -0,0 +1,62 @@
+// MIT License
+//
+// Copyright (c) 2023 被风吹过的夏天
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package com.melody.map.tencent_compose.render
+
+import android.content.Context
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import com.tencent.tencentmap.mapsdk.maps.TencentMap
+import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor
+import com.tencent.tencentmap.mapsdk.maps.model.MarkerOptions
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.Cluster
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterItem
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.ClusterManager
+import com.tencent.tencentmap.mapsdk.vector.utils.clustering.view.DefaultClusterRenderer
+
+/**
+ * CustomClusterRenderer
+ * @author 被风吹过的夏天
+ * @email developer_melody@163.com
+ * @github: https://github.com/TheMelody/OmniMap
+ * created 2023/03/01 15:34
+ */
+internal class CustomClusterRenderer(
+ context: Context?,
+ private val clusterColor: Color?,
+ private val clusterItemIcon: BitmapDescriptor?,
+ tencentMap: TencentMap?,
+ clusterManager: ClusterManager?
+) : DefaultClusterRenderer(context, tencentMap, clusterManager) {
+
+ override fun getColor(value: Int): Int {
+ // 修改聚合点的圆颜色
+ return clusterColor?.toArgb()?:super.getColor(value)
+ }
+
+ override fun onBeforeClusterItemRendered(p0: ClusterItem?, p1: MarkerOptions?) {
+ super.onBeforeClusterItemRendered(p0, p1?.apply {
+ // 修改单个聚合点Marker的图标
+ clusterItemIcon?.let { this.icon(it) }
+ })
+ }
+}
\ No newline at end of file
diff --git a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/utils/PathSmoothTool.kt b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/utils/PathSmoothTool.kt
index f6f7174..a794d5a 100644
--- a/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/utils/PathSmoothTool.kt
+++ b/tencent-map-compose/src/main/java/com/melody/map/tencent_compose/utils/PathSmoothTool.kt
@@ -117,31 +117,31 @@ class PathSmoothTool {
/**
* 单点滤波
- * @param lastLoc 上次定位点坐标
- * @param curLoc 本次定位点坐标
+ * @param lastLocation 上次定位点坐标
+ * @param curLocation 本次定位点坐标
* @param intensity 滤波强度(1—5)
* @return 滤波后本次定位点坐标值
*/
- private fun kalmanFilterPoint(lastLoc: LatLng?, curLoc: LatLng, intensity: Int): LatLng? {
- var curLoc: LatLng? = curLoc
- var intensity = intensity
+ private fun kalmanFilterPoint(lastLocation: LatLng?, curLocation: LatLng, intensity: Int): LatLng? {
+ var curLoc: LatLng? = curLocation
+ var newIntensity = intensity
if (pdelt_x == 0.0 || pdelt_y == 0.0) {
initial()
}
var kalmanLatlng: LatLng? = null
- if (lastLoc == null || curLoc == null) {
- return kalmanLatlng
+ if (lastLocation == null || curLoc == null) {
+ return null
}
- if (intensity < 1) {
- intensity = 1
- } else if (intensity > 5) {
- intensity = 5
+ if (newIntensity < 1) {
+ newIntensity = 1
+ } else if (newIntensity > 5) {
+ newIntensity = 5
}
- for (j in 0 until intensity) {
+ for (j in 0 until newIntensity) {
kalmanLatlng = kalmanFilter(
- lastLoc.longitude,
+ lastLocation.longitude,
curLoc!!.longitude,
- lastLoc.latitude,
+ lastLocation.latitude,
curLoc.latitude
)
curLoc = kalmanLatlng
@@ -185,8 +185,8 @@ class PathSmoothTool {
private fun initial() {
pdelt_x = 0.001
pdelt_y = 0.001
- // mdelt_x = 0;
-// mdelt_y = 0;
+ // mdelt_x = 0;
+ //mdelt_y = 0;
mdelt_x = 5.698402909980532E-4
mdelt_y = 5.698402909980532E-4
}