Android开发:实现第三方地图跳转

966 阅读3分钟

前言

这个需求可能比较常见。当用户点击地图跳转的时候,先检查自身设备是否有对应的地图软件。然后将其进行展示。点击时,将地图信息(经度/维度/目的地)包装起来传给对应软件。

先上效果图:

1.jpg

2.jpg

第二张是点击跳转之后的图。

P.S:因为项目里我还接了高德地图的sdk。所以我这边的做法是,如果自身设备有地图软件,就显示对应项,没有就跳到高德sdk的内置导航。

代码

首先是布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#99000000"
    android:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/ll_close"
        android:background="@drawable/solid_white_radius_top12"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/ll_baidu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:padding="15dp"
                android:text="百度地图"
                android:textColor="#000000"
                android:textSize="18sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:background="#e6e6e6" />

        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_gaode"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:padding="15dp"
                android:text="高德地图"
                android:textColor="#000000"
                android:textSize="18sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:background="#e6e6e6" />

        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_tengxun"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:padding="15dp"
                android:text="腾讯地图"
                android:textColor="#000000"
                android:textSize="18sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:background="#e6e6e6" />

        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_close"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="9dp"
            android:background="#f5f5f5" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#ffffff"
            android:gravity="center"
            android:padding="15dp"
            android:text="取消"
            android:textColor="#000000"
            android:textSize="18sp" />
    </LinearLayout>

</RelativeLayout>

(效果就是上图那个弹窗了)

第一步:检查自身设备有什么地图软件

//1.百度地图包名
public static final String BAIDUMAP_PACKAGENAME = "com.baidu.BaiduMap";
//2.高德地图包名
public static final String GAODEMAP_PACKAGENAME = "com.autonavi.minimap";
//3.腾讯地图包名
public static final String QQMAP_PACKAGENAME = "com.tencent.map";
private static final String[] MAP_PACKAGES = {BAIDUMAP_PACKAGENAME, GAODEMAP_PACKAGENAME, QQMAP_PACKAGENAME};

/**
 * 检查手机上是否安装了指定的软件
 *
 * @param packageNames 可变参数 String[]
 * @return 目标软件中已安装的列表
 */
public static List<String> checkInstalledPackage(Context context, String... packageNames) {

    //获取packageManager
    final PackageManager packageManager = context.getPackageManager();
    //获取所有已安装程序的包信息
    List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
    //用于存储
    List<String> newPackageNames = new ArrayList<>();
    int count = packageNames.length;

    if (packageInfos.size() > 0) {

        outermost:
        for (String packageName : packageNames) {
            for (int i = 0; i < packageInfos.size(); i++) {
                String packageInfo = packageInfos.get(i).packageName;
                if (packageInfo.contains(packageName)) {
                    newPackageNames.add(packageName);
                    if (newPackageNames.size() == count) {
                        break outermost;//这里使用了循环标记,跳出外层循环
                    }
                }
            }
        }
    }
    //判断packageNames中是否有目标程序的包名,有TRUE,没有FALSE
    return newPackageNames;
}

这里只列举三个常用的,也可以自己加想要的,拿到包名就行。

第二步:调用及弹框的实现

/**
 * 参数的key
 * 高德的坐标系 "gd_lng" (高德_经度)、"gd_lat"(纬度)、"destination"(目的地名称)
 */
private static final String GCJO2_LNG = "gd_lng";
private static final String GCJO2_LAT = "gd_lat";
private static final String DESTINATION = "destination";

public static void onNai(Poi end, Context context, View view) {
    List<String> packages = checkInstalledPackage(context, MAP_PACKAGES);
    if (packages.size() == 0) {//本机没有地图软件,启动高德地图的sdk导航
        //构建导航组件配置类,没有传入起点,所以起点默认为 “我的位置”
        AmapNaviParams params = new AmapNaviParams(null, null, end, AmapNaviType.DRIVER, AmapPageType.ROUTE);
        //启动导航组件
        AmapNaviPage.getInstance().showRouteActivity(context, params, null);
    } else {
        Map<String, Object> arg = new HashMap<>();
        arg.put(GCJO2_LAT, end.getCoordinate().latitude);
        arg.put(GCJO2_LNG, end.getCoordinate().longitude);
        arg.put(DESTINATION, end.getName());
        onMap(context, view, packages, arg);
    }
}

首先根据自身设备是否拥有地图软件,来判断怎么走

Poi是高德sdk的一个类。使用者其实也可以自己定义一个bean,有经纬度和目的地信息就够了

private static View popupWindowMap;
private static LinearLayout llBaidu;
private static LinearLayout llGaode;
private static LinearLayout llTengxun;
private static LinearLayout llClose;
private static PopupWindow popupWindow = null;

/**
 * 
 * @param context
 * @param view
 * @param packages 存在的包名集合
 * @param arg 地图参数
 */
private static void onMap(Context context, View view, List<String> packages, Map<String, Object> arg) {
    if (popupWindowMap == null) {
        LayoutInflater layoutInflater = LayoutInflater.from(context);
        popupWindowMap = layoutInflater.inflate(R.layout.popup_window_map_choice, null);
        llBaidu = popupWindowMap.findViewById(R.id.ll_baidu);
        llGaode = popupWindowMap.findViewById(R.id.ll_gaode);
        llTengxun = popupWindowMap.findViewById(R.id.ll_tengxun);
        llClose = popupWindowMap.findViewById(R.id.ll_close);

    }
    popupWindow = new PopupWindow(popupWindowMap, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    llClose.setOnClickListener(v -> popupWindow.dismiss());
    llBaidu.setVisibility(View.GONE);
    llGaode.setVisibility(View.GONE);
    llTengxun.setVisibility(View.GONE);

    for (String str : packages) {
        switch (str) {
            case BAIDUMAP_PACKAGENAME:
                llBaidu.setVisibility(View.VISIBLE);
                llBaidu.setOnClickListener(v -> invokeBaiDuMap(context, arg));
                break;
            case GAODEMAP_PACKAGENAME:
                llGaode.setVisibility(View.VISIBLE);
                llGaode.setOnClickListener(v -> invokeGaoDeMap(context, arg));
                break;
            case QQMAP_PACKAGENAME:
                llTengxun.setVisibility(View.VISIBLE);
                llTengxun.setOnClickListener(v -> invokeQQMap(context, arg));
                break;
        }
    }
    popupWindow.dismiss();
    popupWindow.setFocusable(true);
    popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
}

弹框实现——根据存在包名判断对应项是否显示并添加事件

自己想改或者加东西的话,可以把LinearLayout 改成RecyclerView

第三步:各地图软件的跳转

本质就是在对应地图的开发文档里找到uri跳转实现。自己包装下数据丢给对应app

    /**
     * 调用百度地图----------------
     *
     * @param context 上下文对象
     * @param arg     参数
     */
    private static void invokeBaiDuMap(Context context, Map arg) {
        try {
            Uri uri = Uri.parse("baidumap://map/geocoder?" +
                    "location=" + arg.get(GCJO2_LAT) + "," + arg.get(GCJO2_LNG) +
                    "&name=" + arg.get(DESTINATION) + //终点的显示名称
                    "&coord_type=gcj02");//坐标 (百度同样支持他自己的db0911的坐标,但是高德和腾讯不支持)
            Intent intent = new Intent();
            intent.setPackage(BAIDUMAP_PACKAGENAME);
            intent.setData(uri);

            context.startActivity(intent);
        } catch (Exception e) {
//            Logger.e(TAG, e.getMessage());
        }
    }

    /**
     * 调用高德地图
     *
     * @param context 上下文对象s
     * @param arg     经纬度参数map
     */
    private static void invokeGaoDeMap(Context context, Map arg) {
        try {
            Uri uri = Uri.parse("androidamap://route?sourceApplication={你的应用名称}" +
                    "&dlat=" + arg.get(GCJO2_LAT)//终点的纬度
                    + "&dlon=" + arg.get(GCJO2_LNG)//终点的经度
                    + "&dname=" + arg.get(DESTINATION)//终点的显示名称
                    + "&dev=0&m=0&t=0");
            Intent intent = new Intent("android.intent.action.VIEW", uri);
            intent.addCategory("android.intent.category.DEFAULT");

            context.startActivity(intent);
        } catch (Exception e) {
//            Logger.e(TAG, e.getMessage());
        }
    }

    /**
     * 调用腾讯地图
     *
     * @param context 上下文对象s
     * @param arg     经纬度参数map
     */
    private static void invokeQQMap(Context context, Map arg) {
        try {
            Uri uri = Uri.parse("qqmap://map/routeplan?type=drive" +
                    "&to=" + arg.get(DESTINATION)//终点的显示名称 必要参数
                    + "&tocoord=" + arg.get(GCJO2_LAT) + "," + arg.get(GCJO2_LNG)//终点的经纬度
                    + "&referer={你的应用名称}");
            Intent intent = new Intent();
            intent.setData(uri);

            context.startActivity(intent);
        } catch (Exception e) {
//            Logger.e(TAG, e.getMessage());
        }
    }

到这,一个简单的地图跳转功能也就实现了。代码可能不尽完美有着很多瑕疵,也欢迎大家讨论批判。如果对你有所帮助,点个赞再走呗,感谢。