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 🗺 =============== -LICENSE issues forks stars 稀土掘金 知乎 - - +LICENSE issues forks stars 稀土掘金 知乎 CSDN 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 }