地图在很多应用中都会用到,而百度地图官方的开发者文档很多地方让人摸不着头脑,喵喵喵?而且主要是用在activity中,如果要在fragment中使用地图,还是有一些小坑要注意一下。所以在这里详细记录了一下自己使用百度地图开发的流程。
(悄咪咪说一句,据说高德的开发者文档要写的好一些,但是瞄了一下,其实主要流程都差不多,随缘随缘
1.配置
获取百度地图 SDK开发密钥 点击申请链接添加链接描述。然后访问API控制台页面,若未登录百度账号,将会进入百度账号登录页面。打开API控制台点击创建应用,开始申请开发密钥,应用名称可以随便填,应用类型要选“Android SDK”,然后输入开发版SHA1和发布版SHA1和包名。注册和获取密钥这里不赘述。
添加SDK:jar + so 创建Android项目,在这里下载android定位sdk开发资源添加链接描述,解压后将libs中的jar和so放置到工程中相应的位置。如图:
配置build.gradle文件主要加了sourceSets和implementation files(‘libs/BaiduLBS_Android.jar’)
,注意位置。
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "s.wd.myapplication"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets{
main{
jniLibs.srcDir'libs'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.google.android.gms:play-services-plus:16.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation files('libs/BaiduLBS_Android.jar')
}
添加AK在Application标签中增加如下代码:
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="AK" >
</meta-data>
添加定位权限声明
- 在Application标签中声明service组件
<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote"> </service>
- 在Application标签中添加权限
<!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 这个权限用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!-- 用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- 访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SD卡读取权限,用户写入离线定位数据-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
如图:
2.地图显示
先在布局xml文件中添加地图控件
<com.baidu.mapapi.map.MapView
android:id="@+id/mmap"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.baidu.mapapi.map.MapView>
然后在自己的application或者activity或者fragment中初始化 SDK,因为在应用程序创建时初始化 SDK引用的Context 是全局变量,在SDK各功能组件使用之前都需要调用SDKInitializer.initialize(getApplicationContext());因此建议将该方法放在Application的初始化方法中
package s.wd.myapplication.application;
import android.app.Application;
import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
SDKInitializer.initialize(this);
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
}
}
接下来在fragment的onCreateView()方法中中实例化地图,并且管理地图的生命周期
public class SportFragment extends Fragment {
private MapView mMapView;
private BaiduMap mBaiduMap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_sport, container, false);
mMapView = (MapView)view.findViewById(R.id.mmap);
mBaiduMap = mMapView.getMap();
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
//在fragment执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy();
}
@Override
public void onResume() {
super.onResume();
//在fragment执行onResume时执行mMapView. onResume (),实现地图生命周期管理
mMapView.onResume();
}
@Override
public void onPause() {
super.onPause();
//在fragment执行onPause时执行mMapView. onPause (),实现地图生命周期管理
mMapView.onPause();
}
}
至此,就可以在fragment中显示百度地图了,效果大致如下
对导航栏菜单感兴趣的可以看这篇ViewPager+BottomNavigationView+Fragment实现底部导航栏(Android)
3.定位
在onCreate()中声明LocationClient类对象,该对象初始化需传入Context类型参数。推荐使用getApplicationConext()方法获取全进程有效的Context。核心代码段如下:
public LocationClient mLocationClient = null;
private MyLocationListener myListener = new MyLocationListener();
//BDAbstractLocationListener为7.2版本新增的Abstract类型的监听接口
//原有BDLocationListener接口暂时同步保留。具体介绍请参考后文第四步的说明
public void onCreate() {
mLocationClient = new LocationClient(getApplicationContext());
//声明LocationClient类
mLocationClient.registerLocationListener(myListener);
//注册监听函数
}
在fragment onCreateView中设置mapview的一些地图属性
mMapView = (MapView)view.findViewById(R.id.mmap);
mBaiduMap = mMapView.getMap();
mMapView.showScaleControl(true);//显示比例尺
mMapView.showZoomControls(true);//显示缩放按钮
mMapView.removeViewAt(1);//删除百度地图LoGo
然后配置定位SDK参数,我集成在了自定义的initLocation()函数中
//配置定位SDK参数
private void initLocation() {
LocationClientOption option = new LocationClientOption();
MyLocationListener myLocationListener = new MyLocationListener();
//注册监听函数
mLocationClient.registerLocationListener(myLocationListener);
//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
//可选,设置定位模式,默认高精度
//LocationMode.Hight_Accuracy:高精度;
//LocationMode. Battery_Saving:低功耗;
//LocationMode. Device_Sensors:仅使用设备;
option.setCoorType("bd09ll");
//可选,设置返回经纬度坐标类型,默认GCJ02
//GCJ02:国测局坐标;
//BD09ll:百度经纬度坐标;
//BD09:百度墨卡托坐标;
//海外地区定位,无需设置坐标类型,统一返回WGS84类型坐标
int span=1000;
option.setScanSpan(span);
//可选,设置发起定位请求的间隔,int类型,单位ms
//如果设置为0,则代表单次定位,即仅定位一次,默认为0
//如果设置非0,需设置1000ms以上才有效
option.setOpenGps(true);
//可选,设置是否使用gps,默认false
//使用高精度和仅用设备两种定位模式的,参数必须设置为true
option.setLocationNotify(true);
//可选,设置是否当GPS有效时按照1S/1次频率输出GPS结果,默认false
option.setIgnoreKillProcess(false);
//可选,定位SDK内部是一个service,并放到了独立进程。
//设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)
option.SetIgnoreCacheException(false);
//可选,设置是否收集Crash信息,默认收集,即参数为false
option.setWifiCacheTimeOut(5*60*1000);
//可选,V7.2版本新增能力
//如果设置了该接口,首次启动定位时,会先判断当前Wi-Fi是否超出有效期,若超出有效期,会先重新扫描Wi-Fi,然后定位
option.setEnableSimulateGps(false);
//可选,设置是否需要过滤GPS仿真结果,默认需要,即参数为false
option.setIsNeedAltitude(false);
//设置打开自动回调位置模式,该开关打开后,期间只要定位SDK检测到位置变化就会主动回调给开发者,该模式下开发者无需再关心定位间隔是多少,定位SDK本身发现位置变化就会及时回调给开发者
option.setOpenAutoNotifyMode();
//设置打开自动回调位置模式,该开关打开后,期间只要定位SDK检测到位置变化就会主动回调给开发者
option.setOpenAutoNotifyMode(3000,1, LocationClientOption.LOC_SENSITIVITY_HIGHT);
// 开启定位图层
mBaiduMap.setMyLocationEnabled(true);
mLocationClient.setLocOption(option);
//mLocationClient为第二步初始化过的LocationClient对象
//需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
//开始定位
mLocationClient.start();
// option.setIsNeedAddress(true);
// option.setIsNeedLocationPoiList(true);
}
然后实现BDAbstractLocationListener接口监听定位,并将监听到的location更新到变量mlocation中,记录当前位置
//实现BDAbstractLocationListener接口
public class MyLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
//此处的BDLocation为定位结果信息类,通过它的各种get方法可获取定位相关的全部结果
//以下只列举部分获取经纬度相关(常用)的结果信息
//更多结果信息获取说明,请参照类参考中BDLocation类中的说明
double latitude = location.getLatitude(); //获取纬度信息
double longitude = location.getLongitude(); //获取经度信息
float radius = location.getRadius(); //获取定位精度,默认值为0.0f
String coorType = location.getCoorType();
//获取经纬度坐标类型,以LocationClientOption中设置过的坐标类型为准
int errorCode = location.getLocType();
//获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明
if (errorCode== BDLocation.TypeGpsLocation
|| errorCode == BDLocation.TypeNetWorkLocation) {
navigateTo(location);
mlocation=location;
}
}
}
只需用mLocationClient.start()发起定位便能够从BDAbstractLocationListener监听接口中获取定位结果信息。
但是我想要的是除了一进入地图,地图显示的就是自己定位的位置,还有拖动地图后,点击自定义的定位按钮,地图可以回到定位所在地,(你已经是一个成熟的地图了.jpg
先设置自定义的定位图标的点击事件
iv_request= (ImageView) view.findViewById(R.id.iv_request);
iv_request.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity().getApplicationContext(), "点击事件", Toast.LENGTH_SHORT).show();
isFirstLoc = true;
navigateTo(mlocation);
}
});
其中navigateTo()函数用来更新地图,mlocation为监听到的定位,调用navigateTo(mlocation)即可使地图定位中心显示在mlocation。
private void navigateTo(BDLocation location){
if(location==null){
return;
} else if(isFirstLoc) {
LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
mBaiduMap.animateMapStatus(update);
//zoom设置缩放等级,值越大,地点越详细
MapStatus mMapSta0tus = new MapStatus.Builder()
.target(ll)
.zoom(19)
.build();
//定义MapStatusUpdate对象,以便描述地图状态将要发生的变化
MapStatusUpdate mMapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mMapSta0tus);
//改变地图状态
mBaiduMap.animateMapStatus(mMapStatusUpdate);
isFirstLoc = false;
}
MyLocationData locationData=new MyLocationData.Builder().accuracy(20)//locData.accuracy = location.getRadius();//获取默认误差半径
//accuracy设置精度圈大小
//设置开发者获取到的方向信息,顺时针旋转0-360度
.direction(100).latitude(location.getLatitude()).longitude(location.getLongitude()).build();
mBaiduMap.setMyLocationData(locationData);
}
因为Android版本越高对应用权限管理就越严格了,所以动态权限获取要求开发者在调用涉及相关权限的代码时,使用系统接口来动态的申请相应权限。其中定位需要动态获取的权限有这几个
权限 | 内容 |
---|---|
Manifest.permission.READ_PHONE_STATE; | 获取手机状态 |
Manifest.permission.ACCESS_COARSE_LOCATION; | 获取位置信息 |
Manifest.permission.ACCESS_FINE_LOCATION; | 获取位置信息 |
Manifest.permission.READ_EXTERNAL_STORAGE; | 允许sd卡读权限 |
Manifest.permission.WRITE_EXTERNAL_STORAGE; | 允许sd卡写权限 |
利用Context.checkSelfPermission接口,定义权限获取码,然后触发系统请求权限弹窗申请权限
List<String> permissionList = new ArrayList<>();
if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.READ_PHONE_STATE);
}//获取手机状态
if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}//获取位置信息
if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}//获取位置信息
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}//读写SD卡
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}//读写SD卡
if (!permissionList.isEmpty()) {
String[] permissions = permissionList.toArray(new String[permissionList.size()]);
requestPermissions(permissions, 1);
} else {
requestLocation();
}
然后处理回调函数onRequestPermissionResult
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
//requestCode即所声明的权限获取码,在checkSelfPermission时传入
case 1:
if (grantResults.length > 0) {//获取到权限
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {//需要确保调用定位SDK相关权限均被授权,否则会引起定位失败
Toast.makeText(getActivity(), "发生权限问题", Toast.LENGTH_SHORT).show();
// getActivity().finish();
// return;
}
}
requestLocation();
} else {//没有获取到权限
Toast.makeText(getActivity(), "发生未知错误,换个新手机试试?", Toast.LENGTH_SHORT).show();
getActivity().finish();
}
break;
default:
}
}
至此就实现定位功能了,效果大致如下:
源代码:在fragment中使用百度地图开发
百度开发者文档:添加链接描述