使用百度地图SDK系列除了踩过一些坑,目前没有遇到任何实质性的困难。这是第二篇关于百度地图SDK的博客。推荐阅读:Android 百度地图周边雷达(v3.7.0)
今天主要写百度地图定位和方向传感器结合这么一个中间块案例。
设定到百度地图SDK
为了节省篇幅以及不必要的解释,假设你已经配置好了百度定位SDK。如果没有,请见:http://lbsyun.baidu.com/index.php?title=android-locsdk/guide/buildprojec
按照百度地图SDK,初始化相关组件。
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
//注意该方法要再setContentView方法之前实现
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
testLocation();
在testLocation()里我们注册了mLocationClient的LocationListener以及初始化LocationClientOption。相关代码及详细注释如下
public LocationClient mLocationClient = null;
public BDLocationListener myListener = new MyLocationListener();
private void testLocation() {
mLocationClient = new LocationClient(getApplicationContext()); //声明LocationClient类
mLocationClient.registerLocationListener(myListener); //注册监听函数
/** * 高精度定位模式:这种定位模式下,会同时使用网络定位和GPS定位,优先返回最高精度的定位结果; * 低功耗定位模式:这种定位模式下,不会使用GPS,只会使用网络定位(Wi-Fi和基站定位); * 仅用设备定位模式:这种定位模式下,不需要连接网络,只使用GPS进行定位,这种模式下不支持室内环境的定位。 */
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy
);//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系
int span = 1000;
option.setScanSpan(span);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的
option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要
option.setOpenGps(true);//可选,默认false,设置是否使用gps
option.setLocationNotify(true);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
option.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
option.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
option.setIgnoreKillProcess(false);//可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
option.SetIgnoreCacheException(true);//可选,默认false,设置是否收集CRASH信息,默认收集
option.setEnableSimulateGps(false);//可选,默认false,设置是否需要过滤gps仿真结果,默认需要
mLocationClient.setLocOption(option);
}
细心的同学会看到,这里我们实现了一个自己的Listener。
private class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
//mapview 销毁后不在处理新接收的位置
if (location == null || mBaiduMap == null) {
return;
}
getLocationInfo(location);
// 开启定位图层
mBaiduMap.setMyLocationEnabled(true);
// 构造定位数据
final MyLocationData locData = new MyLocationData.Builder()
.accuracy(location.getRadius())
// 此处设置开发者获取到的方向信息,顺时针0-360
.direction(100)
.latitude(location.getLatitude())
.longitude(location.getLongitude())
.build();
// 设置定位数据
mBaiduMap.setMyLocationData(locData);
if (isFirstGetLocation) {
isFirstGetLocation = false;
// 获取经纬度
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
// 设置地图中心点以及缩放级别
MapStatusUpdate mapStatusUpdate = MapStatusUpdateFactory.newLatLngZoom(latLng, 15);
mBaiduMap.animateMapStatus(mapStatusUpdate);
}
// Log.i(TAG, "latitude:"+mBaiduMap.getLocationData().latitude +
// "\nlongitude:"+mBaiduMap.getLocationData().longitude +
// "\ndirection:"+mBaiduMap.getLocationData().direction);
// 设置定位图层的配置(定位模式,是否允许方向信息,用户自定义定位图标)
mCurrentMarker = BitmapDescriptorFactory.fromResource(R.mipmap.location_pic);
MyLocationConfiguration config = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL,
true, mCurrentMarker);
mBaiduMap.setMyLocationConfigeration(config);
}
}
我们的listener实现了BDLocationListener接口。复写了onReceiveLocation()方法。就像方法的名字,收到定位信息后会调用此方法。因此,在此方法里我们可以实现收到定位信息后自己的逻辑。这样我们就有了一个最初始的百度地图。位置更详细的信息在getLocationInfo()方法里。一般需要的信息都可以在此找到。
private void getLocationInfo(BDLocation location) {
//Receive Location
StringBuffer sb = new StringBuffer(256);
sb.append("time : ");
sb.append(location.getTime());
sb.append("\nerror code : ");
sb.append(location.getLocType());
sb.append("\nlatitude : ");
sb.append(location.getLatitude());
sb.append("\nlontitude : ");
sb.append(location.getLongitude());
sb.append("\nradius : ");
sb.append(location.getRadius());
if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果
sb.append("\nspeed : ");
sb.append(location.getSpeed());// 单位:公里每小时
sb.append("\nsatellite : ");
sb.append(location.getSatelliteNumber());
sb.append("\nheight : ");
sb.append(location.getAltitude());// 单位:米
sb.append("\ndirection : ");
sb.append(location.getDirection());// 单位度
sb.append("\naddr : ");
sb.append(location.getAddrStr());
sb.append("\ndescribe : ");
sb.append("gps定位成功");
} else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果
sb.append("\naddr : ");
sb.append(location.getAddrStr());
//运营商信息
sb.append("\noperationers : ");
sb.append(location.getOperators());
sb.append("\ndescribe : ");
sb.append("网络定位成功");
} else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
sb.append("\ndescribe : ");
sb.append("离线定位成功,离线定位结果也是有效的");
} else if (location.getLocType() == BDLocation.TypeServerError) {
sb.append("\ndescribe : ");
sb.append("服务端网络定位失败,可以反馈IMEI号和大体定位时间到[email protected],会有人追查原因");
} else if (location.getLocType() == BDLocation.TypeNetWorkException) {
sb.append("\ndescribe : ");
sb.append("网络不同导致定位失败,请检查网络是否通畅");
} else if (location.getLocType() == BDLocation.TypeCriteriaException) {
sb.append("\ndescribe : ");
sb.append("无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
}
sb.append("\nlocationdescribe : ");
sb.append(location.getLocationDescribe());// 位置语义化信息,结果类似于“在北京天安门附近”
List<Poi> list = location.getPoiList();// POI数据
if (list != null) {
sb.append("\npoilist size = : ");
sb.append(list.size());
for (Poi p : list) {
sb.append("\npoi= : ");
sb.append(p.getId() + " " + p.getName() + " " + p.getRank());
}
}
Log.i(TAG, sb.toString());
}
至此,我们百度定位SDK的就完事了。假设我们想做到旋转手机的时候旋转location_pic对应的方向怎么办呢?答案就是结合方向传感器啦!中间体,这也是本篇博客的主要所在。
我们只用到X轴坐标:表示手机自身的y轴与地磁场北极方向的角度,即手机顶部朝向与正北方向的角度。(This is the angle between magnetic north and the device’s y axis. )当手机绕着自身的z轴旋转时,该角度值将发生改变。例如该角度值为0时,表示手机顶部指向正北;该角度为90度时,代表手机顶部指向正东;该角度为180度时,代表手机顶部指向正南;该角度为270度时,代表手机顶部指向正西。
package com.dyk.baidumapwork;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
/** * Created by dyk on 2016/3/22. */
public class OrientationSensor implements SensorEventListener {
private static final String TAG = "BaiduMap";
private Context mContext;
private SensorManager mSensorManager;
private Sensor defaultSensor;
private float lastX;
private OrientationSensorListener mOrientationSensorListener;
public OrientationSensor(Context context) {
this.mContext = context;
}
public void start() {
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
defaultSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensorManager.registerListener(this, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
public void stop() {
mSensorManager.unregisterListener(this, defaultSensor);
}
@Override
public void onSensorChanged(SensorEvent event) {
Log.i(TAG,event.values[0]+" "+event.values[1]+" "+event.values[2]);
if (mOrientationSensorListener != null && (Math.abs(event.values[0] - lastX)) > 1){
mOrientationSensorListener.getOrientation(event.values[0]);
lastX = event.values[0];
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public interface OrientationSensorListener{
void getOrientation(float x);
}
public void setOrientationListener(OrientationSensorListener orientationSensorListener){
this.mOrientationSensorListener = orientationSensorListener;
}
}
可以看到,在此我们实现了SensorEventListener接口,并复写了onSensorChanged()和onAccuracyChanged()方法。这里我们用到onSensorChanged的参数event.values[0]获取X轴坐标。这里尴尬的事情来了,这里不断的变化,怎么传递给activity?接口!
这里我们定义了一个OrientationSensorListener接口,只有一个方法。getOrientation(float x)
private OrientationSensorListener mOrientationSensorListener;
public interface OrientationSensorListener{
void getOrientation(float x);
}
public void setOrientationListener(OrientationSensorListener orientationSensorListener){
this.mOrientationSensorListener = orientationSensorListener;
}
在onSensorChanged()方法中我们可以调用OrientationSensorListener#getOrientation(float x),并且把X轴坐标event.values[0]当做参数传递进去,接着在activity里实现接口,获取数据,设定方向。代码如下
private OrientationSensor mOrientationSensor;
/** * 传感器方向 */
private float driection = 0;
mOrientationSensor = new OrientationSensor(this);
mOrientationSensor.start();
private class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
mOrientationSensor.setOrientationListener(new OrientationSensor.OrientationSensorListener() {
@Override
public void getOrientation(float x) {
driection = x;
}
});
final MyLocationData locData = new MyLocationData.Builder()
.accuracy(location.getRadius())
// 此处设置开发者获取到的方向信息,顺时针0-360
.direction(driection)
.latitude(location.getLatitude())
.longitude(location.getLongitude())
.build();
Log.i(TAG, "driection=" + driection);
// 设置定位数据
mBaiduMap.setMyLocationData(locData);
}
}
至此就成功的将方向传感器的数据传递到了百度地图中。最后不要忘了释放资源。
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy();
if (mLocationClient.isStarted()) {
mLocationClient.stop();
}
if (mBaiduMap.isMyLocationEnabled()) {
// 当不需要定位图层时关闭定位图层
mBaiduMap.setMyLocationEnabled(false);
}
//反注册OrientationSensor
if (mOrientationSensor != null) {
mOrientationSensor.stop();
}
}
欢迎访问我的博客及另外两篇关于百度地图SDK的博文,综合使用可以实现更多效果:
我的博客
Android 百度地图周边雷达(v3.7.0)及覆盖物
Android 百度鹰眼轨迹SDK(v2.1.6)