Android 调起第三方导航

Android 调起第三方导航

一、功能说明

实现 Android 应用内调起高德、百度、腾讯地图导航,包含:

  • 自动坐标转换(WGS84/GCJ02/BD09)
  • Android 11+ 包可见性适配
  • 安装检测 + 自动降级
  • 驾车/步行/骑行导航

二、权限配置(必须)

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Android 11+ 包可见性声明 -->
    <queries>
        <package android:name="com.autonavi.minimap" />
        <package android:name="com.baidu.BaiduMap" />
        <package android:name="com.tencent.map" />
    </queries>

    <application>
        <activity android:name=".MainActivity" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

三、坐标转换工具类

CoordinateConverter.kt

import kotlin.math.*

object CoordinateConverter {
    private const val PI = 3.1415926535897932384626
    private const val A = 6378245.0
    private const val EE = 0.00669342162296594323

    fun wgs84ToGcj02(lng: Double, lat: Double): DoubleArray {
        if (outOfChina(lng, lat)) return doubleArrayOf(lng, lat)
        var dLat = transformLat(lng - 105.0, lat - 35.0)
        var dLng = transformLng(lng - 105.0, lat - 35.0)
        val radLat = lat / 180.0 * PI
        var magic = sin(radLat)
        magic = 1 - EE * magic * magic
        val sqrtMagic = sqrt(magic)
        dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI)
        dLng = (dLng * 180.0) / (A / sqrtMagic * cos(radLat) * PI)
        return doubleArrayOf(lng + dLng, lat + dLat)
    }

    fun gcj02ToBd09(lng: Double, lat: Double): DoubleArray {
        val z = sqrt(lng * lng + lat * lat) + 0.00002 * sin(lat * PI * 3000.0 / 180.0)
        val theta = atan2(lat, lng) + 0.000003 * cos(lng * PI * 3000.0 / 180.0)
        return doubleArrayOf(z * cos(theta) + 0.0065, z * sin(theta) + 0.006)
    }

    fun bd09ToGcj02(lng: Double, lat: Double): DoubleArray {
        val x = lng - 0.0065
        val y = lat - 0.006
        val z = sqrt(x * x + y * y) - 0.00002 * sin(y * PI * 3000.0 / 180.0)
        val theta = atan2(y, x) - 0.000003 * cos(x * PI * 3000.0 / 180.0)
        return doubleArrayOf(z * cos(theta), z * sin(theta))
    }

    private fun outOfChina(lng: Double, lat: Double): Boolean {
        return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271
    }

    private fun transformLat(x: Double, y: Double): Double {
        var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x))
        ret += (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0
        ret += (20.0 * sin(y * PI) + 40.0 * sin(y / 3.0 * PI)) * 2.0 / 3.0
        ret += (160.0 * sin(y / 12.0 * PI) + 320 * sin(y * PI / 30.0)) * 2.0 / 3.0
        return ret
    }

    private fun transformLng(x: Double, y: Double): Double {
        var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x))
        ret += (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0
        ret += (20.0 * sin(x * PI) + 40.0 * sin(x / 3.0 * PI)) * 2.0 / 3.0
        ret += (150.0 * sin(x / 12.0 * PI) + 300.0 * sin(x / 30.0 * PI)) * 2.0 / 3.0
        return ret
    }
}

四、导航工具类(核心)

NavigationUtils.kt

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.widget.Toast

object NavigationUtils {
    private const val AMAP = "com.autonavi.minimap"
    private const val BAIDU = "com.baidu.BaiduMap"
    private const val TENCENT = "com.tencent.map"

    fun startNavigation(
        context: Context,
        wgs84Lat: Double,
        wgs84Lng: Double,
        name: String,
        navType: Int = 0,
        prefer: Int = 0
    ) {
        val (lat, lng) = when (prefer) {
            0, 2 -> {
                val gcj = CoordinateConverter.wgs84ToGcj02(wgs84Lng, wgs84Lat)
                gcj[1] to gcj[0]
            }
            1 -> {
                val gcj = CoordinateConverter.wgs84ToGcj02(wgs84Lng, wgs84Lat)
                val bd = CoordinateConverter.gcj02ToBd09(gcj[0], gcj[1])
                bd[1] to bd[0]
            }
            else -> wgs84Lat to wgs84Lng
        }

        val intent = when (prefer) {
            0 -> amapIntent(lat, lng, name, navType)
            1 -> baiduIntent(lat, lng, name, navType)
            2 -> tencentIntent(lat, lng, name, navType)
            else -> amapIntent(lat, lng, name, navType)
        }

        val pkg = when (prefer) {
            0 -> AMAP
            1 -> BAIDU
            2 -> TENCENT
            else -> AMAP
        }

        if (isInstalled(context, pkg)) {
            context.startActivity(intent)
        } else {
            tryFallback(context, lat, lng, name, navType, prefer)
        }
    }

    private fun amapIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
        val t = when (type) { 0 -> "0"; 1 -> "1"; 2 -> "2"; else -> "0" }
        val uri = Uri.parse("amapuri://route/plan/?dlat=$lat&dlng=$lng&dname=$name&dev=0&t=$t")
        return Intent(Intent.ACTION_VIEW, uri)
    }

    private fun baiduIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
        val mode = when (type) { 0 -> "driving"; 1 -> "walking"; 2 -> "riding"; else -> "driving" }
        val uri = Uri.parse("baidumap://map/direction/?destination=latlng:$lat,$lng|name:$name&mode=$mode")
        return Intent(Intent.ACTION_VIEW, uri)
    }

    private fun tencentIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
        val t = when (type) { 0 -> "drive"; 1 -> "walk"; 2 -> "bike"; else -> "drive" }
        val uri = Uri.parse("qqmap://map/routeplan?type=$t&to=$name&tocoord=$lat,$lng")
        return Intent(Intent.ACTION_VIEW, uri)
    }

    private fun isInstalled(context: Context, pkg: String): Boolean {
        return try {
            context.packageManager.getApplicationInfo(pkg, 0)
            true
        } catch (e: Exception) {
            false
        }
    }

    private fun tryFallback(ctx: Context, lat: Double, lng: Double, name: String, type: Int, prefer: Int) {
        when (prefer) {
            0 -> when {
                isInstalled(ctx, BAIDU) -> ctx.startActivity(baiduIntent(lat, lng, name, type))
                isInstalled(ctx, TENCENT) -> ctx.startActivity(tencentIntent(lat, lng, name, type))
                else -> toast(ctx)
            }
            1 -> when {
                isInstalled(ctx, AMAP) -> ctx.startActivity(amapIntent(lat, lng, name, type))
                isInstalled(ctx, TENCENT) -> ctx.startActivity(tencentIntent(lat, lng, name, type))
                else -> toast(ctx)
            }
            2 -> when {
                isInstalled(ctx, AMAP) -> ctx.startActivity(amapIntent(lat, lng, name, type))
                isInstalled(ctx, BAIDU) -> ctx.startActivity(baiduIntent(lat, lng, name, type))
                else -> toast(ctx)
            }
            else -> toast(ctx)
        }
    }

    private fun toast(context: Context) {
        Toast.makeText(context, "请安装高德/百度/腾讯地图", Toast.LENGTH_SHORT).show()
    }
}

五、调用示例

// 天安门 WGS84 坐标
NavigationUtils.startNavigation(
    context = this,
    wgs84Lat = 39.908823,
    wgs84Lng = 116.397470,
    name = "天安门",
    navType = 0,        // 0驾车 1步行 2骑行
    prefer = 0          // 0高德 1百度 2腾讯
)

六、常见问题

1. 已安装高德却提示未安装

原因:Android 11+ 包可见性限制
解决:必须在 AndroidManifest.xml 添加 <queries>

2. 导航位置偏移

原因:坐标系不统一

  • 高德 / 腾讯:GCJ02
  • 百度:BD09
  • GPS 原始:WGS84
    解决:使用 CoordinateConverter 自动转换

3. 调起失败

检查包名:

  • 高德:com.autonavi.minimap
  • 百度:com.baidu.BaiduMap
  • 腾讯:com.tencent.map

七、测试命令

adb shell pm list packages | grep com.autonavi.minimap

View.OnClickListener onClickListener=new View.OnClickListener() { @Override public void onClick(View view) { switch (view.getId()) { case R.id.baidu_btn: if (MDMUtil.appIsInstalled( getContext(),"com.baidu.BaiduMap")) {//传入指定应用包名 try { double[] gd_lat_lon ; if(RoutingXModel.isGpslatlon){ gd_lat_lon= gaoDeToBaidu(xModel.poc_lon,xModel.poc_lat); }else{ gd_lat_lon= new double[2]; gd_lat_lon[0]=xModel.poc_lon; gd_lat_lon[1]=xModel.poc_lat; } Intent intent = Intent.getIntent("intent://map/direction?" + "destination=latlng:" + gd_lat_lon[1] + "," + gd_lat_lon[0]+ "|name:我的目的地" + //终点 "&mode=driving&" + //导航路线方式 "&src=appname#Intent;scheme=bdapp;package=com.baidu.BaiduMap;end"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); //启动调用 } catch (URISyntaxException e) { Log.e("intent", e.getMessage()); } } else {//未安装 //market为路径,id为包名 //显示手机上所有的market商店 Toast.makeText(getContext(), "您尚未安装百度地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.baidu.BaiduMap"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); if (intent.resolveActivity(getContext().getPackageManager()) != null){ getContext().startActivity(intent); } } dismiss(); break; case R.id.gaode_btn: if (MDMUtil.appIsInstalled( getContext(),"com.autonavi.minimap")) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); double[] gd_lat_lon ; if(!RoutingXModel.isGpslatlon){ gd_lat_lon= bdToGaoDe(xModel.poc_lat,xModel.poc_lon); }else{ gd_lat_lon= new double[2]; gd_lat_lon[0]=xModel.poc_lon; gd_lat_lon[1]=xModel.poc_lat; } //将功能Scheme以URI的方式传入data Uri uri = Uri.parse("androidamap://navi?sourceApplication=appname&poiname=fangheng&lat;=" + gd_lat_lon[1] + "&lon;=" + gd_lat_lon[0] + "&dev=0&style=4"); intent.setData(uri); //启动该页面即可 getContext().startActivity(intent); } else { Toast.makeText(getContext(), "您尚未安装高德地图", Toast.LENGTH_LONG).show(); Uri uri = Uri.parse("market://details?id=com.autonavi.minimap"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); if (intent.resolveActivity(getContext().getPackageManager()) != null){ getContext().startActivity(intent); } } dismiss(); break; case R.id.tencent_btn: Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); double[] gd_lat_lon ; if(!RoutingXModel.isGpslatlon){ gd_lat_lon= bdToGaoDe(xModel.poc_lat,xModel.poc_lon); }else{ gd_lat_lon= new double[2]; gd_lat_lon[0]=xModel.poc_lon; gd_lat_lon[1]=xModel.poc_lat; } //将功能Scheme以URI的方式传入data Uri uri = Uri.parse("qqmap://map/routeplan?type=drive&to;=我的目的地&tocoord;=" + gd_lat_lon[1]+ "," + gd_lat_lon[0]); intent.setData(uri); if (intent.resolveActivity(getContext().getPackageManager()) != null) { //启动该页面即可 getContext().startActivity(intent); } else { Toast.makeText(getContext(), "您尚未安装腾讯地图", Toast.LENGTH_LONG).show(); } dismiss(); break; case R.id.cancel_btn2: dismiss(); break; } } };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiegwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值