实现功能: 后台定位 地图长按添加marker得到当前位置经纬度坐标 创建地理围栏(以marker所在位置为中心,半径默认设置为100米) 通过BroadcastReceiver监听当前位置与围栏的位置关系 实现过程:
1. 后台定位
为了最大程度的增加定位进程的存活率,百度Android定位SDK提供后台持续定位的能力,可在应用退后台的状态下,持续记录位置信息。
首先初始化定位信息,主要是设置坐标类型等一些基本属性
// 定位初始化
private void initLocationSDK() {
mClient = new LocationClient(this);
LocationClientOption mOption = new LocationClientOption();
mOption.setScanSpan(5000);
mOption.setCoorType("bd09ll");//设置坐标类型
mOption.setIsNeedAddress(true);//设置是否需要地址信息,默认为无地址。
mOption.setOpenGps(true);
mClient.setLocOption(mOption);
mClient.registerLocationListener(myLocationListener);
}
初始化完成后开启后台定位功能,此时将会以5秒/次进行后台定位,及时你的应用处于后台,只要没有被清理依旧会执行定位过程,这样就可实现当应用处于后台仍然会执行的功能,比如在导航过程中进行当前坐标实时实施上传。
private void startBackgroundLocation() {
if (isEnableLocInForeground) {
//关闭后台定位(true:通知栏消失;false:通知栏可手动划除)
mClient.disableLocInForeground(true);
isEnableLocInForeground = false;
mForegroundBtn.setText("前台定位");
mClient.stop();
} else {
//开启后台定位
mClient.enableLocInForeground(1, notification);
isEnableLocInForeground = true;
mForegroundBtn.setText("后台定位");
mClient.start();
}
}
为地图设置长按监听事件,在地图上长按选点并将该点设置成为地理围栏的中心点
public void longClickAddMaker() {
mBaiduMap.clear(); //清除地图上的marker标志
mBaiduMap.setOnMapLongClickListener(new BaiduMap.OnMapLongClickListener() {
@Override
public void onMapLongClick(LatLng latLng) {
if (latLng != null) {
if (isOnLongClickEnable) {
latitude = latLng.latitude;
longitude = latLng.longitude;
//Dest_BD09LL_End = new LatLng(latLng.latitude, latLng.longitude);
Toast.makeText(DeleveryInfo.this, "" + latitude + " fff " + longitude, Toast.LENGTH_SHORT).show();
mBitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_openmap_mark); //用来构造InfoWindow
//响应点击的OnInfoWindowClickListener----为marker添加点击事件
InfoWindow.OnInfoWindowClickListener listener = new InfoWindow.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick() {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// popuInfoFlag = 1;
// dhPopupView();
// }
Toast.makeText(DeleveryInfo.this, "点击了添加的marker图标", Toast.LENGTH_LONG).show();
}
};
//构造InfoWindow
//point 描述的位置点 -100 InfoWindow相对于point在y轴的偏移量
InfoWindow mInfoWindow = new InfoWindow(mBitmap, new LatLng(latitude, longitude), -100, listener);
mBaiduMap.showInfoWindow(mInfoWindow);//使InfoWindow生效
isOnLongClickEnable = false; //禁止添加marker
}
}
}
});
}
创建地理围栏 (默认半径为100米)
private void initGeoFenceClient() {
fenceClient = new GeoFenceClient(getApplicationContext());// 1 创建地理围栏客户端
IntentFilter filter = new IntentFilter(); //注册
filter.addAction(GEOFENCE_BROADCAST_ACTION);
registerReceiver(mGeoFenceReceiver, filter);
fenceClient.createPendingIntent(GEOFENCE_BROADCAST_ACTION);
fenceClient.isHighAccuracyLoc(true); // 在即将触发侦听行为时允许开启高精度定位模式(开启gps定位,gps定位结果优先)
fenceClient.setGeoFenceListener(DeleveryInfo.this);
fenceClient.setActivateAction(GeoFenceClient.GEOFENCE_IN);
}
private void addRoundFence() {
// String customId = etCustomId.getText().toString();
// String radiusStr = etRadius.getText().toString();
if (null == new LatLng(latitude, longitude) || TextUtils.isEmpty("100")||latitude ==0.0) {
Toast.makeText(getApplicationContext(), "参数不全", Toast.LENGTH_SHORT)
.show();
return;
}
DPoint centerPoint = new DPoint(latitude, longitude);
//fenceRadius = Float.parseFloat(radiusStr);
fenceClient.addGeoFence(centerPoint, GeoFenceClient.BD09LL, 100, "1");//fenceRadius, "1");
}
创建BroadcastReceiver来监听当前坐标与围栏的关系
private BroadcastReceiver mGeoFenceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 接收广播
if (intent.getAction().equals(GEOFENCE_BROADCAST_ACTION)) {
Bundle bundle = intent.getExtras();
String customId = bundle
.getString(GeoFence.BUNDLE_KEY_CUSTOMID);
String fenceId = bundle.getString(GeoFence.BUNDLE_KEY_FENCEID);
GeoFence geoFence = bundle.getParcelable(GeoFence.BUNDLE_KEY_FENCE);
int status = bundle.getInt(GeoFence.BUNDLE_KEY_FENCESTATUS);
int locType = bundle.getInt(GeoFence.BUNDLE_KEY_LOCERRORCODE);
StringBuffer sb = new StringBuffer();
switch (status) {
case GeoFence.INIT_STATUS_IN:
sb.append("围栏初始状态:在围栏内");
Toast.makeText(DeleveryInfo.this, "在里面", Toast.LENGTH_SHORT).show();
break;
case GeoFence.INIT_STATUS_OUT:
sb.append("围栏初始状态:在围栏外");
Toast.makeText(DeleveryInfo.this, "在围栏外", Toast.LENGTH_SHORT).show();
break;
case GeoFence.STATUS_LOCFAIL:
sb.append("定位失败,无法判定目标当前位置和围栏之间的状态");
Toast.makeText(DeleveryInfo.this, "无法判定目标当前位置和围栏之间的状态", Toast.LENGTH_SHORT).show();
break;
case GeoFence.STATUS_IN:
sb.append("进入围栏 ");
break;
case GeoFence.STATUS_OUT:
sb.append("离开围栏 ");
break;
default:
break;
}
if (status != GeoFence.STATUS_LOCFAIL) {
if (!TextUtils.isEmpty(customId)) {
sb.append(" customId: " + customId);
}
sb.append(" fenceId: " + fenceId);
}
String str = sb.toString();
Message msg = Message.obtain();
msg.obj = str;
msg.what = 2;
handler2.sendMessage(msg);
}
}
};
xml文件中只有一个button按钮,就不展示了。
完整代码:public class DeleveryInfo extends AppCompatActivity implements View.OnClickListener,
BaiduMap.OnMapClickListener, GeoFenceListener {
double latitude; //长按的精度
double longitude;// 长按的纬度
// 地理围栏的广播action
private static final String GEOFENCE_BROADCAST_ACTION = "liyue.edu.ncst.cn.mymap.DeleveryInfo";
private GeoFenceClient fenceClient; //创建地理围栏
private float fenceRadius; //要创建的围栏的半径
private HashMap fenceMap = new HashMap();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
SDKInitializer.setCoordType(CoordType.BD09LL);
setContentView(R.layout.activity_delevery_info);
initViews();
initLocationSDK(); //初始化定位
// settingLocInForeground();//设置后台定位
startBackgroundLocation(); //开始后台定位
initGeoFenceClient();
}
// 定位初始化
private void initLocationSDK() {
mClient = new LocationClient(this);
LocationClientOption mOption = new LocationClientOption();
mOption.setScanSpan(5000);
mOption.setCoorType("bd09ll");//设置坐标类型
mOption.setIsNeedAddress(true);//设置是否需要地址信息,默认为无地址。
mOption.setOpenGps(true);
mClient.setLocOption(mOption);
mClient.registerLocationListener(myLocationListener);
}
private void startBackgroundLocation() {
if (isEnableLocInForeground) {
//关闭后台定位(true:通知栏消失;false:通知栏可手动划除)
mClient.disableLocInForeground(true);
isEnableLocInForeground = false;
mForegroundBtn.setText("前台定位");
mClient.stop();
} else {
//开启后台定位
mClient.enableLocInForeground(1, notification);
isEnableLocInForeground = true;
mForegroundBtn.setText("后台定位");
mClient.start();
}
}
class MyLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation == null || mMapView == null) {
return;
}
if (isFirstLoc) { //如果是第一次定位
LatLng ll = null;
ll = getMostAccuracyLocation(bdLocation);
if (ll == null) {
return;
}
isFirstLoc = false;
points.add(ll);//加入集合
last = ll;
if (ll.latitude != 4.9E-324) //定位失败时不显示
locateAndZoom(bdLocation, ll);
}
LatLng point = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
if (DistanceUtil.getDistance(last, point) > 1) {//比较两次定位点间的距离
if (bdLocation.getLatitude() != 4.9E-324) { //判断当前定位点是否正确
points.add(point);//如果要运动完成后画整个轨迹,位置点都在这个集合中
last = point;
locateAndZoom(bdLocation, point); //当非第一次定位时也要执行定位图标显示
OverlayOptions dotOption = new DotOptions().center(point).color(0xAAA9A9A9);
mBaiduMap.addOverlay(dotOption);
Dest_BD09LL_Start = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());//设置导航的起点点坐标
myLocation = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
}
}
}
}
private LatLng getMostAccuracyLocation(BDLocation location) {
if (location.getRadius() > 45) {//gps位置精度大于40米的点直接弃用
return null;
}
LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
if (DistanceUtil.getDistance(last, ll) > 5) {
last = ll;
points.clear();//有任意连续两点位置大于10,重新取点
return null;
}
points.add(ll);
last = ll;
//有5个连续的点之间的距离小于10,认为gps已稳定,以最新的点为起始点
if (points.size() >= 2) {
points.clear();
return ll;
}
return null;
}
private void locateAndZoom(final BDLocation location, LatLng ll) {
locData = new MyLocationData.Builder().accuracy(0)
// 此处设置开发者获取到的方向信息,顺时针0-360
.direction(location.getDirection()).latitude(location.getLatitude())
.longitude(location.getLongitude()).build();
mBaiduMap.setMyLocationData(locData);
MapStatus.Builder builder = new MapStatus.Builder();
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
}
private void initGeoFenceClient() {
fenceClient = new GeoFenceClient(getApplicationContext());// 1 创建地理围栏客户端
IntentFilter filter = new IntentFilter(); //注册
filter.addAction(GEOFENCE_BROADCAST_ACTION);
registerReceiver(mGeoFenceReceiver, filter);
fenceClient.createPendingIntent(GEOFENCE_BROADCAST_ACTION);
fenceClient.isHighAccuracyLoc(true); // 在即将触发侦听行为时允许开启高精度定位模式(开启gps定位,gps定位结果优先)
fenceClient.setGeoFenceListener(DeleveryInfo.this);
fenceClient.setActivateAction(GeoFenceClient.GEOFENCE_IN);
}
Object lock = new Object();
void drawFence2Map() {
new Thread() {
@Override
public void run() {
try {
synchronized (lock) {
if (null == fenceList || fenceList.isEmpty()) {
return;
}
for (GeoFence fence : fenceList) {
if (fenceMap.containsKey(fence.getFenceId())) {
continue;
}
drawFence(fence);
fenceMap.put(fence.getFenceId(), fence);
}
}
} catch (Throwable e) {
}
}
}.start();
}
private void drawFence(GeoFence fence) {
switch (fence.getType()) {
case GeoFence.TYPE_ROUND:
drawCircle(fence, false);
break;
}
}
private void drawCircle(GeoFence fence, boolean isPoi) {
LatLng center;
int radius;
if (isPoi) {
BDLocation bdLocation = new BDLocation();
bdLocation.setLatitude(fence.getCenter().getLatitude());
bdLocation.setLongitude(fence.getCenter().getLongitude());
BDLocation tempLocation = LocationClient
.getBDLocationInCoorType(bdLocation, BDLocation.BDLOCATION_GCJ02_TO_BD09LL);
center = new LatLng(tempLocation.getLatitude(),
tempLocation.getLongitude());
} else {
center = new LatLng(latitude, longitude);
}
radius = (int) fence.getRadius();
// 绘制一个圆形
if (center == null) {
return;
}
mBaiduMap.addOverlay(new CircleOptions().center(center)
.radius(radius)
.fillColor(0xAA0000FF) // 填充颜色
.stroke(new Stroke(5, 0xAA00ff00)));
}
@SuppressLint("HandlerLeak")
Handler handler2 = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
StringBuffer sb = new StringBuffer();
sb.append("添加围栏成功");
String customId = (String) msg.obj;
if (!TextUtils.isEmpty(customId)) {
sb.append("customId: ").append(customId);
}
Toast.makeText(getApplicationContext(), sb.toString(),
Toast.LENGTH_SHORT).show();
drawFence2Map();
break;
case 1:
int errorCode = msg.arg1;
Toast.makeText(getApplicationContext(),
"添加围栏失败,errorcode = " + errorCode, Toast.LENGTH_SHORT).show();
break;
case 2:
break;
default:
break;
}
}
};
private BroadcastReceiver mGeoFenceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 接收广播
if (intent.getAction().equals(GEOFENCE_BROADCAST_ACTION)) {
Bundle bundle = intent.getExtras();
String customId = bundle
.getString(GeoFence.BUNDLE_KEY_CUSTOMID);
String fenceId = bundle.getString(GeoFence.BUNDLE_KEY_FENCEID);
GeoFence geoFence = bundle.getParcelable(GeoFence.BUNDLE_KEY_FENCE);
int status = bundle.getInt(GeoFence.BUNDLE_KEY_FENCESTATUS);
int locType = bundle.getInt(GeoFence.BUNDLE_KEY_LOCERRORCODE);
StringBuffer sb = new StringBuffer();
switch (status) {
case GeoFence.INIT_STATUS_IN:
sb.append("围栏初始状态:在围栏内");
Toast.makeText(DeleveryInfo.this, "在里面", Toast.LENGTH_SHORT).show();
break;
case GeoFence.INIT_STATUS_OUT:
sb.append("围栏初始状态:在围栏外");
Toast.makeText(DeleveryInfo.this, "在围栏外", Toast.LENGTH_SHORT).show();
break;
case GeoFence.STATUS_LOCFAIL:
sb.append("定位失败,无法判定目标当前位置和围栏之间的状态");
Toast.makeText(DeleveryInfo.this, "无法判定目标当前位置和围栏之间的状态", Toast.LENGTH_SHORT).show();
break;
case GeoFence.STATUS_IN:
sb.append("进入围栏 ");
break;
case GeoFence.STATUS_OUT:
sb.append("离开围栏 ");
break;
case GeoFence.STATUS_STAYED:
sb.append("在围栏内停留超过10分钟 ");
break;
default:
break;
}
if (status != GeoFence.STATUS_LOCFAIL) {
if (!TextUtils.isEmpty(customId)) {
sb.append(" customId: " + customId);
}
sb.append(" fenceId: " + fenceId);
}
String str = sb.toString();
Message msg = Message.obtain();
msg.obj = str;
msg.what = 2;
handler2.sendMessage(msg);
}
}
};
List fenceList = new ArrayList();
@Override
public void onGeoFenceCreateFinished(final List geoFenceList,
int errorCode, String customId) {
Message msg = Message.obtain();
if (errorCode == GeoFence.ADDGEOFENCE_SUCCESS) {
fenceList.addAll(geoFenceList);
msg.obj = customId;
msg.what = 0;
} else {
msg.arg1 = errorCode;
msg.what = 1;
}
handler2.sendMessage(msg);
}
private void addRoundFence() {
// String customId = etCustomId.getText().toString();
// String radiusStr = etRadius.getText().toString();
if (null == new LatLng(latitude, longitude) || TextUtils.isEmpty("100")||latitude ==0.0) {
Toast.makeText(getApplicationContext(), "参数不全", Toast.LENGTH_SHORT)
.show();
return;
}
DPoint centerPoint = new DPoint(latitude, longitude);
//fenceRadius = Float.parseFloat(radiusStr);
fenceClient.addGeoFence(centerPoint, GeoFenceClient.BD09LL, 100, "1");//fenceRadius, "1");
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.getMap().clear();
mMapView.onDestroy();
mMapView = null;
mClient.disableLocInForeground(true); // 关闭前台定位服务
mClient.unRegisterLocationListener(myLocationListener); // 取消之前注册的 BDAbstractLocationListener 定位监听函数
mBaiduMap.setMyLocationEnabled(false);
mClient.stop();// 停止定位sdk
try {
unregisterReceiver(mGeoFenceReceiver); //注销广播
} catch (Throwable e) {
}
if (null != fenceClient) {
fenceClient.removeGeoFence(); // 地理围栏客户端
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
最后,如果要想实现地理围栏的话要到百度地图官网下载相应的Android 定位 SDK ,注意要选择全量定位。
百度地图官网关于后台定位的讲解
百度地图官网关于地理围栏的讲解
作者:小李也有春天