文章目录
1.基于位置的服务简介
基于位置的服务所围绕的核心就是要先确定出用户所在的位置。通常有两种技术方式可以实现:一种是通过GPS定位,一种是通过网络定位。
GPS定位的工作原理是基于手机内置的GPS硬件直接和卫星交互来获取当前的经纬度信息,这种方式精确度非常高,但缺点是只能在室外使用,室内基本无法接收到卫星信号。网络定位的工作原理是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,再通过三角定位确定出一个大概位置,这种定位方式精确度一般,但优点是在室内室外都可以使用。
接下来,我们就来学习一下百度在LBS方面提供的丰富多彩的功能。
2.申请API Key
要想在自己的应用程序中使用百度的LBS功能,首先你必须申请一个API Key。首先你必须拥有一个百度账号,倘若没有那就快去注册一个吧。
有了百度账号后,我们就可以申请成为一名百度开发者了,在这里填写一些注册信息即可,如下图所示。
接下来点击提交,会显示如下图的界面。
接着点击“去我的邮箱”,将会进入到我们刚才填写的邮箱当中,这时收件箱中应该会有一封刚刚收到的邮件,这就是百度给我们发送的验证邮件,点击邮件当中的链接就可以完成注册了。
这样你就成为了一名百度开发者了。接着访问这个地址,然后同意百度开发者的协议。之后点击应用管理下的我的应用,接下来点击创建应用就可以去申请API Key了,应用名称可以随便填,应用类型选择Android SDK,启用服务保持默认即可,如下图所示。
那么,这个发布版SHA1和开发板SHA1又是个什么东西呢?这是我们申请API Key所必须填写的一个字段,他指的是打包程序时所用签名文件的SHA1指纹,可以通过Android Studio查看。打开Android Studio中的任意一个项目,点击右侧工具栏的Gradle–>项目名–>:app–>Tasks–>android,如下图所示。
这里展示了一个Android Studio项目中所有内置的Gradle Tasks,其中signingReport这个Task就可以用来查看签名文件信息。双击signingReport,结果如下图所示。
其中就有我们所需要的SHA1指纹了,当然你的Android Studio中显示的指纹和我的肯定是不一样的。另外需要注意,我们现在得到的是一个开发版的SHA1指纹,不过因为我们暂时还没有一个发布版的SHA1指纹,因此这两个值都填成一样的就可以了。最后还剩一个包名选项,这里将自己程序的包名填入即可。接下来点击提交,应用就应该创建好了,如下图所示。
其中访问应用(AK)下的字段,就是我们申请到的API Key了,有了它我们就可以进行后续的LBS开发工作了。
3.使用百度定位。
3.1准备LBS SDK
首先,我们还需要将百度LBS开放平台的SDK准备好,下载地址。接下来我们会用到基础地图和定位功能这两个SDK,将它们勾选上,然后点击“开发包”下载按钮即可。
下载完成后对该压缩包解压,其中会有一个libs目录,这里的内容就是我们所要的一切了,如下图所示。
libs目录下的内容又分为两部分,BaiduLBS_Android.jar这个文件是java层要使用的,其它子目录下的so文件时Native层要用到的。接下来我们只需要将libs目录下的每一个文件放置到正确的位置即可。
首先观察一下当前的项目结构,你会发现app模块下面有一个libs目录,这里就是用来存放所有jar包的,我们将BaiduLBS_Android.jar复制到这里。
接下来展开src/main目录,右击该目录再创建一个名为jniLibs的目录,这里就是专门用来存放so文件的,然后把压缩包里的其它所有目录直接复制到这里。
另外,虽然所有新创建的项目中,app/build.gradle文件都会默认配置以下这段声明:
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
}
这表明会将libs目录下的所有以.jar结尾的文件添加到当前项目的引用中。但是由于我们是直接将jar包复制到libs目录下的,并没有修改gradle文件,因此不会弹出我们熟悉的Sync Now提示。这时必须手动点击一下Sync按钮,点击Sync按钮之后,libs目录下的jar文件就会多出一个向右的箭头,这就表示项目已经能引用到这些jar包了。
好了,这样我们就把LBS的SDK都准备好了,接下来就可以开始编码了。
3.2确定自己位置的经纬度
首先我们要修改AndroidManifest.xml文件中的代码:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.lbstest">
//声明权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
//配置我们的API Key
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="A46gI0ot4CGph3bK7e9o5Bg0w4TX0Hw5" /> //此处填入的就是我们申请到的API Key
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//注册LBS SDK中的服务
<service android:name="com.baidu.location.f" android:enabled="true"
android:process=":remote" />
</application>
</manifest>
接下来我们来学习获取自己位置经纬度的方法:
- 1.获取LocationClient类的实例。
- 2.定义一个类继承BDLocationListener接口,我们就可以在该类中调用相关方法来获取我们的位置信息。
- 3.给LocationClient绑定我们刚刚创建的类。
- 4.调用LocationClient的start()方法,我们创建的类中的代码就会被执行了。
代码展示:
public class LocationActivity extends AppCompatActivity {
private LocationClient client;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
client=new LocationClient(getApplicationContext());//步骤一
client.registerLocationListener(new MyListener());//步骤三
setContentView(R.layout.activity_location);
//首先我们还要对危险权限进行处理,而这里也是借助List来同时对多个危险权限进行处理
List<String> permissionList=new ArrayList<>();
if(ContextCompat.checkSelfPermission(LocationActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if(ContextCompat.checkSelfPermission(LocationActivity.this, Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}
if(ContextCompat.checkSelfPermission(LocationActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(!permissionList.isEmpty()){
String[] permission=permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(LocationActivity.this,permission,1);
}else {
requestLocation();
}
}
public void requestLocation(){
client.start();//步骤四
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if(grantResults.length>0){
for(int result:grantResults){
if(result!=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
finish();
return;
}
}
requestLocation();
}else {
Toast.makeText(this,"发生未知错误",Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
//步骤二
public class MyListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
double x=bdLocation.getLatitude();//获取纬度的方法
double y=bdLocation.getLongitude();//获取经线的方法
bdLocation.getLocType();//获取定位方式的方法
}
}
}
上面的代码中还有一些瑕疵,那就是默认情况下,调用LocationClient的start()方法只会定位一次,如果我们正在快速移动,怎样才能实现更新当前的位置呢?
我们可以通过LocationClientOption这个类来改变这一默认行为。
代码展示:
public class LocationActivity extends AppCompatActivity {
...
public void requestLocation(){
initLocation();
client.start();
}
private void initLocation(){
LocationClientOption option=new LocationClientOption();
option.setScanSpan(5000);//设置更新间隔
client.setLocOption(option);
}
@Override
protected void onDestroy() {
super.onDestroy();
client.stop();//最后要记得,当活动销毁时要调用此方法来停止定位
}
...
}
3.3选择定位模式
我们可以调用LocationClientOption类的setLocationMode()方法来指定百度LBS SDK的定位模式。
共有3种模式可以选择:Hight_Accuracy高精确度模式,会在GPS信号正常的情况下优先使用GPS定位,在无法接收到GPS信号的时候使用网络定位;Battery_Saving节电模式,只会使用网络进行定位;Device_Sensors传感器模式,只会使用GPS进行定位。其中,Hight_Accuracy是默认的模式。
代码展示:
public class LocationActivity extends AppCompatActivity {
...
private void initLocation(){
LocationClientOption option=new LocationClientOption();
option.setScanSpan(5000);
option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
client.setLocOption(option);
}
...
}
3.4看得懂的位置信息
相信对于大多数人来说给出经纬度信息,我们可能并不知道它所对应的地方在哪。所以为了能更加直观地阅读,我们还需要学习一下如何获取看得懂的位置信息。
方法步骤:
- 1.调用LocationClientOption类的setIsNeedAddress(true)方法,表示我们需要获取当前位置的详细地址信息。
- 2.调用LocationClient类的相关方法来获取当前地址的详细信息。
代码展示:
public class LocationActivity extends AppCompatActivity {
...
private void initLocation(){
LocationClientOption option=new LocationClientOption();
option.setScanSpan(5000);
option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
option.setIsNeedAddress(true);
client.setLocOption(option);
}
public class MyListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
double x=bdLocation.getLatitude();
double y=bdLocation.getLongitude();
bdLocation.getLocType();
String nation=bdLocation.getCountry();//获取当前所在国家的方法
String province=bdLocation.getProvince();//获取当前所在省的方法
String city=bdLocation.getCity();//获取当前所在市的方法
String area=bdLocation.getDistrict();//获取当前所在区的方法
String street=bdLocation.getStreet();//获取当前所在街的方法
}
}
...
}
注意:由于获取地址信息一定需要用到网络,因此即使我们将定位模式调整为Device_Sensors,也会自动开启网络定位功能。
4.百度地图
4.1让地图显示出来
方法步骤:
- 1.使用MapView控件。
- 2.调用SDKInitializer.initialize()方法来进行初始化操作。
- 3.获取MapView的实例。
- 4.重写onResume(),onPause(),onDestory()这3个方法,在这里对MapView进行管理,以保证资源能够即使释放掉。
代码展示:
//步骤一
<com.baidu.mapapi.map.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"/>
public class LocationActivity extends AppCompatActivity {
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());//步骤二
setContentView(R.layout.activity_location);
mapView=findViewById(R.id.map_view);//步骤三
...
}
//步骤四
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}
注意:步骤二的初始化操作一定要在setContentView()方法前调用,不然的话就会出错。
4.2移动到我的位置
地图虽然是成功显示出来了,但这只是一张默认地图而已,而在日常生活中我们通常想要看到的是自己所在位置的地图。接下来,我们就来学习如何在地图中快速移动到自己的位置。
百度LBS SDK的API中提供了一个BaiduMap类,它是地图的总控制器,有了BaiduMap后,我们就可以对地图进行各种各样的操作了,比如设置地图的缩放级别以及将地图移动到某一个经纬度上,而这些设置往往都是要配合MapStatusUpdate类来实现的。(百度地图将缩放级别的取值范围限定在3到19之间)
方法步骤:
- 1.调用MapView的getMap()方法获取BaiduMap类的实例。
- 2.获得MapStatusUpdate类的实例,并完成一些设置。
- 3.将设置绑定给MapView。
代码展示:
public class LocationActivity extends AppCompatActivity {
private LocationClient client;
private MapView mapView;
private BaiduMap baiduMap;
private boolean isFirstLocate=true;//该变量防止多次调用animateMapStatus()方法,因为将地图移动到我们当前的位置只需要在程序第一次定位时调用即可
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
client=new LocationClient(getApplicationContext());
client.registerLocationListener(new MyListener());
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_location);
mapView=findViewById(R.id.map_view);
baiduMap=mapView.getMap();//步骤一
...
}
private void navigateTo(BDLocation location){
if(isFirstLocate){
LatLng ll=new LatLng(location.getLatitude(),location.getLongitude());//该类用于存储经纬度信息
MapStatusUpdate update= MapStatusUpdateFactory.newLatLng(ll);//步骤二(将地图移动到某一经纬度上)
baiduMap.animateMapStatus(update);//步骤三
update=MapStatusUpdateFactory.zoomTo(16f);//将地图的缩放级别调整为16
baiduMap.animateMapStatus(update);
isFirstLocate=false;
}
}
public class MyListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
if(bdLocation.getLocType()==BDLocation.TypeNetWorkLocation||bdLocation.getLocType()==BDLocation.TypeGpsLocation){
navigateTo(bdLocation);
}
}
}
...
}
4.3让“我”显示在地图上
目前,虽然我们将地图移动到了我们当前的位置,不过一般情况下手机上的地图都应该有一个小光标,用于显示设备当前所在的位置,并且如过设备在移动的话,那么这个光标也会跟着一起移动。接下来,我们就来学习如何将“我”显示在地图上。
方法步骤:
- 1.获取MyLocationData.Builder类的实例。
- 2.再将当前位置的经纬度信息封装给MyLocationData.Builder类。
- 3.再通过MyLocationData.Builder类获取MyLocationData类的实例。
- 4.最后调用BaiduMap的setMyLocationData()方法,传入MyLocationData类的实例即可。
代码展示:
public class LocationActivity extends AppCompatActivity {
private LocationClient client;
private MapView mapView;
private BaiduMap baiduMap;
private boolean isFirstLocate=true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
client=new LocationClient(getApplicationContext());
client.registerLocationListener(new MyListener());
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_location);
mapView=findViewById(R.id.map_view);
baiduMap=mapView.getMap();
baiduMap.setMyLocationEnabled(true);//在此之前,要开启baiduMap的setMyLocation功能
...
}
private void navigateTo(BDLocation location){
...
MyLocationData.Builder locationBuilder=new MyLocationData.Builder();//步骤一
locationBuilder.longitude(location.getLatitude());//步骤二
locationBuilder.longitude(location.getLongitude());
MyLocationData locationData=locationBuilder.build();//步骤三
baiduMap.setMyLocationData(locationData);//步骤四
}
@Override
protected void onDestroy() {
super.onDestroy();
client.stop();
mapView.onDestroy();
baiduMap.setMyLocationEnabled(false);//最后还要记得关闭baiduMap的setMyLocation功能
}
}
注:各位读者在自己复现时可能会出现定位不准确的问题,读者可以移步这篇博客查看解决方法。
5.自定义标记点
5.1在地图上绘制标记点及其点击事件
我们在做地图开发时,时常需要在地图的某处打上我们自己的标记点,以此来实现更多复杂的功能。
方法步骤:
- 1.定义Maker坐标点
- 2.构建Maker图标
- 3.构建MarkerOption,用于在地图上添加Marker
- 4.在地图上添加Marker,并显示
代码展示:
//步骤一
LatLng point = new LatLng(39.944251, 116.494996);
//步骤二
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.marker_custom);
//步骤三
OverlayOptions option = new MarkerOptions()
.position(point) //必传参数
.icon(bitmap) //必传参数
//步骤四
mBaiduMap.addOverlay(option);
//Maker的点击事件
mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
//点击后执行的逻辑
return false;
}
});
5.2标记点的删除
由于百度地图的SDK中并没有提供Maker的删除方法,因此我们只能采用重绘的方法来实现标记点的删除。
解决方法:
首先我们使用baiduMap.clear()方法来删除地图上的所有标记点,再重新绘制标记点(本次就可以不绘制待删除的标记点),以此来实现标记点的删除。
6.步行路线规划
在实现步行路线规划功能之前我们先要准备好相应的SDK(本博客3.1章有说明)加入我们的项目中;另外我们还需要下载此Demo并将其中的assets和com.baidu.mapapi.overlayutil两个文件复制到我们的项目中。(注:步行路径规划起终点距离不应超过 100 公里)
在做好以上两步之后,我们就可以在我们的项目中实现步行路线规划的功能了。
方法步骤:
- 1.创建路径规划实例
- 2.创建路线规划检索结果监听器
- 3.设置路线规划检索监听器
- 4.准备起终点信息
- 5.发起检索
- 6.释放检索实例
代码展示:
//步骤一
RoutePlanSearch mSearch = RoutePlanSearch.newInstance();
//步骤二
OnGetRoutePlanResultListener listener=new OnGetRoutePlanResultListener() {
@Override
public void onGetWalkingRouteResult(WalkingRouteResult walkingRouteResult) {
//创建WalkingRouteOverlay实例
WalkingRouteOverlay overlay = new WalkingRouteOverlay(mBaiduMap);
if (walkingRouteResult.getRouteLines().size() > 0) {
//获取路径规划数据,(以返回的第一条数据为例)
//为WalkingRouteOverlay实例设置路径数据
overlay.setData(walkingRouteResult.getRouteLines().get(0));
//在地图上绘制WalkingRouteOverlay
overlay.addToMap();
}
}
//其余是其它方式的路线规划,在此不做处理
@Override
public void onGetTransitRouteResult(TransitRouteResult transitRouteResult) {
}
@Override
public void onGetMassTransitRouteResult(MassTransitRouteResult massTransitRouteResult) {
}
@Override
public void onGetDrivingRouteResult(DrivingRouteResult drivingRouteResult) {
}
@Override
public void onGetIndoorRouteResult(IndoorRouteResult indoorRouteResult) {
}
@Override
public void onGetBikingRouteResult(BikingRouteResult bikingRouteResult) {
}
};
//步骤三
mSearch.setOnGetRoutePlanResultListener(listener);
//步骤四
PlanNode stNode=PlanNode.withLocation(startLatLng);
PlanNode enNode=PlanNode.withLocation(aimLatLng);
//步骤五
mSearch.walkingSearch((new WalkingRoutePlanOption())
.from(stNode)
.to(enNode));
//步骤六
mSearch.destroy();