前言
这个需求可能比较常见。当用户点击地图跳转的时候,先检查自身设备是否有对应的地图软件。然后将其进行展示。点击时,将地图信息(经度/维度/目的地)包装起来传给对应软件。
先上效果图:
第二张是点击跳转之后的图。
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());
}
}
到这,一个简单的地图跳转功能也就实现了。代码可能不尽完美有着很多瑕疵,也欢迎大家讨论批判。如果对你有所帮助,点个赞再走呗,感谢。