基于地理位置服务的Android平台的开发对Android移动开发来说是非常重要的,基于地理位置服务的Android平台的开发是主要用于Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。比如说天气预报程序可以根据用户所在的位置自动选择城市,发微博的时候我们可以向朋友晒一下自己的地理位置,不认识路的时候随时打开地图就可以查询路线;如果你出门打车用滴滴或Uber打车,你可以看到附近司机所在的位置,当你创建订单时司机可以获取你所在的位置;如果想获取自己所在位置的周边的服务,我们可以打开高德地图或美团就能获取到的周边的小吃、银行、医院和电影院等。
那么如何在Android项目里调用基于百度地图API实现定位的开发呢?
要想在自己的应用程序里加入百度地图的功能,首先必须申请一个API Key。你得拥有一个百度账号才能申请,申请好百度账号后那么就得在百度地图开放平台注册成为一名百度开发者。登录百度账号,并打开http://lbsyun.baidu.com/这个网址,在这里填写一些注册信息即可,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ckl48RTz-1573318889378)(https://img-blog.csdn.net/20171207201949633?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnVrYWltZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CVQ3tegh-1573318889378)(https://img-blog.csdn.net/20171207180356927?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnVrYWltZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
由于这是一个刚刚注册的账号,所以目前的应用列表是空的。接下来点击创建应用就可以去申请API Key了,应用名称可以随便填,应用类型选择Android SDK,启用服务保持默认即可(最好全选),包名就是填写你要申请API Key应用到哪个应用上的包名,包名必须一致,否则应用上的地图的画面加载不出来。如下图所示:
那么剩下的发布版SHA1和开发版SHA1是什么意思呢?这是我们申请API Key所必须填写的字段,发布版SHA1就是你自己对你所开发的App进行数字加密签名的指纹证书,防止别人对的App进行反编译的操作,也是为了证明该App是你个人开发的;开发版SHA1是指在Android Studio或Eclipse上创建一个项目是默认生成的一个数字指纹证书,注意它们都是唯一的,如果Android Studio或Eclipse再重新安装Android SDK时会又随机生成不同的开发版SHA1数字指纹证书。
那么如何在Android Studio上获取开发版SHA1值和发布版SHA1值?
要想获取发布版的SHA1的值,必须先对项目进行签名,具体是如何对项目进行签名的,详情请看我之前发表的博客:Android移动开发-如何在AndroidStudio里进行对应用程序进行签名,项目签名后可以直接用Android Studio提供的命令控制台Terminal输入命令时输入“cd 自己应用签名的jks文件保存的文件目录”然后接着回车,比如:cd D:\AndroidStudio\AndroidKey\BaiduMapTest1,接着在命令行里输入keytool -list -v -keystore jks文件名,比如:keytool -list -v -keystore BaiduMapTest.jks,接着按回车,然后输入秘钥,回车(秘钥库口令是看不到的)。如下图所示:
然后在百度地图开发者中心的创建应用里分别输入开发版和发布版的SHA1的值的后点击提交,就能申请到了百度地图的API Key了。如下图所示:
现在正是趁热打铁的好时机,新建一个百度地图测试的项目,项目名称为BaiduMapTest,包名为com.fukaimei.BaiduMapTest(注意:必须在百度地图里申请API Key时填写的包名一致),在开始编码之前,我们还需要先将百度地图Android版的SDK准备好,下载地址为:http://lbsyun.baidu.com/index.php?title=sdk/download&action#selected=location_all,然后点击一键下载按钮即可。
下载完成后对压缩包解压,并在Android Studio里做相应的环境配置。
第一步:在工程app/libs目录下放入baidumapapi_vX_X_X.jar包,在src/main/目录下新建jniLibs目录,工程会自动加载src目录下的so动态库,放入libBaiduMapSDK_vX_X_X_X.so如下图所示,注意jar和so的前3位版本号必须一致,并且保证使用一次下载的文件夹中的两个文件,不能不同功能组件的jar或so交叉使用。
so的配置也可以参考demo给出的目录结构,如下图所示,在app工程路径下,新建libs,并在libs目录下放入对应不同CPU架构的so文件。这样工程并不会自动加载libs下的so,需在gradle编译时,通过加入代码: jniLibs.srcDir ‘libs’ 来说明so的路径为该libs路径。
第二步:工程配置还需要把jar包集成到自己的工程中,如图上图所示,放入libs目录下。对于每个jar文件,右键-选择Add As Library,导入到工程中。对应在build.gradle生成工程所依赖的jar文件说明,如下图所示:
集成地图SDK的应用,在打包混淆的时候,需要注意与地图SDK相关的方法不可被混淆。混淆方法如下:
-keep class com.baidu.** {*;}
-keep class vi.com.** {*;}
-dontwarn com.baidu.**
保证百度类不能被混淆,否则会出现网络不可用等运行时异常。
百度地图SDK为开发者提供了便捷的显示百度地图数据的接口,通过以下几步操作,即可在您的应用中使用百度地图数据:
第一步:创建并配置工程;
第二步:在AndroidManifest中添加开发密钥、所需权限等信息;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
//注意该方法要再setContentView方法之前实现
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
}
}
注意:在SDK各功能组件使用之前都需要调用。
SDKInitializer.initialize(getApplicationContext());,因此我们建议该方法放在Application的初始化方法中。
public class MainActivity extends Activity {
MapView mMapView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
//注意该方法要再setContentView方法之前实现
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
//获取地图控件引用
mMapView = (MapView) findViewById(R.id.bmapView);
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理
mMapView.onPause();
}
}
(1)在AndroidManifest.xml清单文件中声明SERVICE组件,每个APP拥有自己单独的定位SERVICE,如下图所示:
package com.fukaimei.baidumaptest;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.widget.PopupMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.MapStatus;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.MyLocationConfiguration;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.model.LatLng;
public class MainActivity extends Activity {
private static final int BAIDU_READ_PHONE_STATE = 100;
private MapView mMapView = null;
private BaiduMap mBaiduMap;
private LocationClient mlocationClient;
private MylocationListener mlistener;
private Context context;
private double mLatitude;
private double mLongitude;
private float mCurrentX;
private Button mGetMylocationBN;
PopupMenu popup = null;
//自定义图标
private BitmapDescriptor mIconLocation;
private MyOrientationListener myOrientationListener;
//定位图层显示方式
private MyLocationConfiguration.LocationMode locationMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
this.context = this;
initView();
//判断是否为Android 6.0 以上的系统版本,如果是,需要动态添加权限
if (Build.VERSION.SDK_INT >= 23) {
showLocMap();
} else {
initLocation();//initLocation为定位方法
}
}
private void initView() {
mMapView = (MapView) findViewById(R.id.id_bmapView);
mBaiduMap = mMapView.getMap();
//根据给定增量缩放地图级别
MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(18.0f);
mBaiduMap.setMapStatus(msu);
MapStatus mMapStatus;//地图当前状态
MapStatusUpdate mMapStatusUpdate;//地图将要变化成的状态
mMapStatus = new MapStatus.Builder().overlook(-45).build();
mMapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mMapStatus);
mBaiduMap.setMapStatus(mMapStatusUpdate);
mGetMylocationBN = (Button) findViewById(R.id.id_bn_getMyLocation);
mGetMylocationBN.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getMyLocation();
}
});
}
/**
* 定位方法
*/
private void initLocation() {
locationMode = MyLocationConfiguration.LocationMode.NORMAL;
//定位服务的客户端。宿主程序在客户端声明此类,并调用,目前只支持在主线程中启动
mlocationClient = new LocationClient(this);
mlistener = new MylocationListener();
//注册监听器
mlocationClient.registerLocationListener(mlistener);
//配置定位SDK各配置参数,比如定位模式、定位时间间隔、坐标系类型等
LocationClientOption mOption = new LocationClientOption();
//设置坐标类型
mOption.setCoorType("bd09ll");
//设置是否需要地址信息,默认为无地址
mOption.setIsNeedAddress(true);
//设置是否打开gps进行定位
mOption.setOpenGps(true);
//设置扫描间隔,单位是毫秒,当<1000(1s)时,定时定位无效
int span = 1000;
mOption.setScanSpan(span);
//设置 LocationClientOption
mlocationClient.setLocOption(mOption);
//初始化图标,BitmapDescriptorFactory是bitmap 描述信息工厂类.
mIconLocation = BitmapDescriptorFactory
.fromResource(R.drawable.icon_geo);
myOrientationListener = new MyOrientationListener(context);
//通过接口回调来实现实时方向的改变
myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
@Override
public void onOrientationChanged(float x) {
mCurrentX = x;
}
});
}
@Override
protected void onStart() {
super.onStart();
//开启定位
mBaiduMap.setMyLocationEnabled(true);
if (!mlocationClient.isStarted()) {
mlocationClient.start();
}
myOrientationListener.start();
}
@Override
protected void onStop() {
super.onStop();
//停止定位
mBaiduMap.setMyLocationEnabled(false);
mlocationClient.stop();
myOrientationListener.stop();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
public void getMyLocation() {
LatLng latLng = new LatLng(mLatitude, mLongitude);
MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
mBaiduMap.setMapStatus(msu);
}
public void onPopupMenuClick(View v) {
// 创建PopupMenu对象
popup = new PopupMenu(this, v);
// 将R.menu.menu_main菜单资源加载到popup菜单中
getMenuInflater().inflate(R.menu.menu_main, popup.getMenu());
// 为popup菜单的菜单项单击事件绑定事件监听器
popup.setOnMenuItemClickListener(
new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.id_map_common:
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
break;
case R.id.id_map_site:
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
break;
case R.id.id_map_traffic:
if (mBaiduMap.isTrafficEnabled()) {
mBaiduMap.setTrafficEnabled(false);
item.setTitle("实时交通(off)");
} else {
mBaiduMap.setTrafficEnabled(true);
item.setTitle("实时交通(on)");
}
break;
case R.id.id_map_mlocation:
getMyLocation();
break;
case R.id.id_map_model_common:
//普通模式
locationMode = MyLocationConfiguration.LocationMode.NORMAL;
break;
case R.id.id_map_model_following:
//跟随模式
locationMode = MyLocationConfiguration.LocationMode.FOLLOWING;
break;
case R.id.id_map_model_compass:
//罗盘模式
locationMode = MyLocationConfiguration.LocationMode.COMPASS;
break;
}
return true;
}
});
popup.show();
}
/**
* 所有的定位信息都通过接口回调来实现
*/
public class MylocationListener implements BDLocationListener {
//定位请求回调接口
private boolean isFirstIn = true;
//定位请求回调函数,这里面会得到定位信息
@Override
public void onReceiveLocation(BDLocation bdLocation) {
//BDLocation 回调的百度坐标类,内部封装了如经纬度、半径等属性信息
//MyLocationData 定位数据,定位数据建造器
/**
* 可以通过BDLocation配置如下参数
* 1.accuracy 定位精度
* 2.latitude 百度纬度坐标
* 3.longitude 百度经度坐标
* 4.satellitesNum GPS定位时卫星数目 getSatelliteNumber() gps定位结果时,获取gps锁定用的卫星数
* 5.speed GPS定位时速度 getSpeed()获取速度,仅gps定位结果时有速度信息,单位公里/小时,默认值0.0f
* 6.direction GPS定位时方向角度
* */
mLatitude = bdLocation.getLatitude();
mLongitude = bdLocation.getLongitude();
MyLocationData data = new MyLocationData.Builder()
.direction(mCurrentX)//设定图标方向
.accuracy(bdLocation.getRadius())//getRadius 获取定位精度,默认值0.0f
.latitude(mLatitude)//百度纬度坐标
.longitude(mLongitude)//百度经度坐标
.build();
//设置定位数据, 只有先允许定位图层后设置数据才会生效,参见 setMyLocationEnabled(boolean)
mBaiduMap.setMyLocationData(data);
//配置定位图层显示方式,三个参数的构造器
/**
* 1.定位图层显示模式
* 2.是否允许显示方向信息
* 3.用户自定义定位图标
* */
MyLocationConfiguration configuration
= new MyLocationConfiguration(locationMode, true, mIconLocation);
//设置定位图层配置信息,只有先允许定位图层后设置定位图层配置信息才会生效,参见 setMyLocationEnabled(boolean)
mBaiduMap.setMyLocationConfigeration(configuration);
//判断是否为第一次定位,是的话需要定位到用户当前位置
if (isFirstIn) {
//地理坐标基本数据结构
LatLng latLng = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
//描述地图状态将要发生的变化,通过当前经纬度来使地图显示到该位置
MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
//改变地图状态
mBaiduMap.setMapStatus(msu);
isFirstIn = false;
Toast.makeText(context, "您当前的位置为:" + bdLocation.getAddrStr(),Toast.LENGTH_LONG).show();
}
}
}
/**
* Android 6.0 以上的版本的定位方法
*/
public void showLocMap() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "没有权限,请手动开启定位权限", Toast.LENGTH_SHORT).show();
// 申请一个(或多个)权限,并提供用于回调返回的获取码(用户定义)
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE
}, BAIDU_READ_PHONE_STATE);
} else {
initLocation();
}
}
//Android 6.0 以上的版本申请权限的回调方法
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
// requestCode即所声明的权限获取码,在checkSelfPermission时传入
case BAIDU_READ_PHONE_STATE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 获取到权限,作相应处理(调用定位SDK应当确保相关权限均被授权,否则可能引起定位失败)
initLocation();
} else {
// 没有获取到权限,做特殊处理
Toast.makeText(getApplicationContext(), "获取位置权限失败,请手动开启", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
}
Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(Gitee)