写在前面
本来想使用百度定位的SDK把定位功能实现了,可无奈遇上个坑到写这篇blog为止都还没有解决。故所以把使用百度地图SDK实现简单的POI检索功能和遇到的坑先做一个总结。
下载.os和jar包
坑
- 如果需要使用自定义的功能,最好不要分别下载。
例如之前我只是想先简单的实现POI检索功能,所以只下载了跟百度地图相关的SDK,实现了之后觉得单调,决定加入定位功能的时候再单独去下载了定位相关的SDK,坑就出现了,引入.os和jar之后,
出现java.lang.NoSuchMethodError: No direct method <init>
这样的错误。只要我们下载的时候勾选要下载的SDK统一下载,就不会出现这样的bug了。
2.jar和.os引入之后,仍然无法使用(初始化SDK)。
解决方案是在app的build.gradle加入
android{
...
sourceSets {
main() {
jniLibs.srcDirs = ['libs']
}
}
}
这在地图的文档没有说到,但在定位的文档才可以找到。小小的吐槽一下百度文档...
地图初始化
- 添加密匙 (申请百度账号,密匙在这里就不写了。很简单)
<application>
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="开发者 key" />
</application>
- 添加所需权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
- 在布局xml文件中添加地图控件
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true" />
- 在应用程序创建时初始化 SDK
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
/***
* 初始化定位sdk,建议在Application中创建
*/
SDKInitializer.initialize(getApplicationContext());
}
}
- 管理地图生命周期
@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();
}
}
当然,还要在你的AndroidManifest中把你的Application设置成我们自定义的MyApplication
完成以上步骤运行,就能把地图显示出来,在没实现定位功能之前,初始化位置是在北京。
POI检索
- 初始化POI检索对象
/**
*实例化
*/
PoiSearch mPoiSearch = PoiSearch.newInstance();
/**
*回调
*/
OnGetPoiSearchResultListener poiListener = new OnGetPoiSearchResultListener(){
@Override
public void onGetPoiResult(PoiResult result){
//获取POI检索结果
}
@Override
public void onGetPoiDetailResult(PoiDetailResult result){
//获取Place详情页检索结果
}
@Override
public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {
//获取门址类列表
}
};
/**
*设置监听
*/
mPoiSearch.setOnGetPoiSearchResultListener(poiListener);
-
检索
城市检索
mPoiSearch.searchInCity((new PoiCitySearchOption())
.city("深圳")//城市
.keyword("美食")//检索关键字
.pageNum(0)//分页编码
.pageCapacity(5));//每页容量,默认10条
周边检索
mPoiSearch.searchNearby(new PoiNearbySearchOption()
//搜索结果排序规则,PoiSortType.comprehensive->距离排序
.sortType(PoiSortType.comprehensive) ->综合排序;
.radius(1000)//检索半径范围,单位:米
.location(LatLng location)) //检索位置
区域检索
mPoiSearch.searchInBound(new PoiBoundSearchOption()
.bound(LatLngBounds bound)//检索范围
);
构建LatLngBounds对象
LatLng southwest = new LatLng(latitude - 0.01, longitude - 0.012);// 西南
LatLng northeast = new LatLng(latitude + 0.01, longitude + 0.012);// 东北
LatLngBounds bounds = new LatLngBounds.Builder().include(southwest)
.include(northeast).build();// 得到一个地理范围对象
所有检索的结果都在poiListener中回调
在每一个POI中都包含一个"Uid"的字段,如果我们需要知道某个POI的详情,我们可以
mPoiSearch.searchPoiDetail((new PoiDetailSearchOption()).poiUid(uid));
检索的结果同样是在poiListener中回调
详细的文档内容在这里
在回调中得到的是一条条的信息,纯文本看着多没意思,展示的地图还没用上。那我们就可以给每个检索出来的POI在地图上标注起来,瞬间好玩多了!
- 覆盖物
自定义 PoiOverlay 类
private class MyPoiOverlay extends PoiOverlay {
public MyPoiOverlay(BaiduMap baiduMap) {
super(baiduMap);
}
@Override
public boolean onPoiClick(int index) {
super.onPoiClick(index);
return true;
}
}
在POI检索回调接口中添加自定义的PoiOverlay
public void onGetPoiResult(PoiResult result) {
if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
return;
}
if (result.error == SearchResult.ERRORNO.NO_ERROR) {
mBaiduMap.clear();
//创建PoiOverlay
PoiOverlay overlay = new MyPoiOverlay(mBaiduMap);
//设置overlay可以处理标注点击事件
mBaiduMap.setOnMarkerClickListener(overlay);
//设置PoiOverlay数据
overlay.setData(result);
//添加PoiOverlay到地图中
overlay.addToMap();
overlay.zoomToSpan();
return;
}
}
这样,检索出来的POI在地图上就完成标注了
写到这里,简单的地图的POI功能就完成了。
写在最后
简简单单的POI检索当然不能满足我们日益膨胀的心呀!所以现在努力实现定位的功能,到时候再更新blog,分享给那些还没了解定位但即将使用的筒靴一个开篇指引和遇到的坑。加油!
note:
在覆盖物中设计到两个类:PoiOverlay 和OverlayManager在SDK中是没有的,但在官方的Demo中给出。这里是给没有下载Demo的筒靴的一个帮助,希望可以共同进步!
12月6号更新:
今天什么也没改跑了一遍Demo,原先实现的定位功能突然就好了... 船到桥头自然直呀
贴一下定位的代码,基本照搬官方文档上的代码,就不作过多的解释了
/**
* 封装定位结果和时间的实体类
*
* @author baidu
*/
class LocationEntity {
BDLocation location;
long time;
}
private void initlocalMap() {
mBaiduMap = mMapView.getMap();
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
mBaiduMap.setMapStatus(MapStatusUpdateFactory.zoomTo(15));
mBaiduMap.setMyLocationEnabled(true);
locService = ((MyApplication) getApplication()).locationService;
LocationClientOption mOption = locService.getDefaultLocationClientOption();
mOption.setOpenGps(true);
mOption.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
mOption.setCoorType("bd09ll");
// locService.setLocationOption(mOption);
// locService.registerListener(listener);
// locService.start();
mClient = new LocationClient(this);
mClient.setLocOption(mOption);
mClient.registerLocationListener(listener);
mClient.start();
}
/***
* 定位结果回调,在此方法中处理定位结果
*/
BDLocationListener listener = new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation location) {
if (location != null && (location.getLocType() == 161 || location.getLocType() == 66)) {
Message locMsg = locHander.obtainMessage();
Bundle locData;
locData = Algorithm(location);
if (locData != null) {
locData.putParcelable("loc", location);
locMsg.setData(locData);
locHander.sendMessage(locMsg);
}
}
}
};
/***
* 平滑策略代码实现方法,主要通过对新定位和历史定位结果进行速度评分,
* 来判断新定位结果的抖动幅度,如果超过经验值,则判定为过大抖动,进行平滑处理,若速度过快,
* 则推测有可能是由于运动速度本身造成的,则不进行低速平滑处理 ╭(●`?′●)╯
*
* @param location
* @return Bundle
*/
private Bundle Algorithm(BDLocation location) {
Bundle locData = new Bundle();
double curSpeed = 0;
if (locationList.isEmpty() || locationList.size() < 2) {
LocationEntity temp = new LocationEntity();
temp.location = location;
temp.time = System.currentTimeMillis();
locData.putInt("iscalculate", 0);
locationList.add(temp);
} else {
if (locationList.size() > 5)
locationList.removeFirst();
double score = 0;
for (int i = 0; i < locationList.size(); ++i) {
LatLng lastPoint = new LatLng(locationList.get(i).location.getLatitude(),
locationList.get(i).location.getLongitude());
LatLng curPoint = new LatLng(location.getLatitude(), location.getLongitude());
double distance = DistanceUtil.getDistance(lastPoint, curPoint);
curSpeed = distance / (System.currentTimeMillis() - locationList.get(i).time) / 1000;
score += curSpeed * EARTH_WEIGHT[i];
}
if (score > 0.00000999 && score < 0.00005) { // 经验值,开发者可根据业务自行调整,也可以不使用这种算法
location.setLongitude(
(locationList.get(locationList.size() - 1).location.getLongitude() + location.getLongitude())
/ 2);
location.setLatitude(
(locationList.get(locationList.size() - 1).location.getLatitude() + location.getLatitude())
/ 2);
locData.putInt("iscalculate", 1);
} else {
locData.putInt("iscalculate", 0);
}
LocationEntity newLocation = new LocationEntity();
newLocation.location = location;
newLocation.time = System.currentTimeMillis();
locationList.add(newLocation);
}
return locData;
}
LatLng point;
/***
* 接收定位结果消息,并显示在地图上
*/
private Handler locHander = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
try {
BDLocation location = msg.getData().getParcelable("loc");
int iscal = msg.getData().getInt("iscalculate");
if (location != null) {
point = new LatLng(location.getLatitude(), location.getLongitude());
// 构建Marker图标
BitmapDescriptor bitmap = null;
if (iscal == 0) {
bitmap = BitmapDescriptorFactory.fromResource(R.drawable.huaji); // 非推算结果
} else {
bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_openmap_focuse_mark); // 推算结果
}
// 构建MarkerOption,用于在地图上添加Marker
OverlayOptions option = new MarkerOptions().position(point).icon(bitmap);
// 在地图上添加Marker,并显示
mBaiduMap.addOverlay(option);
mBaiduMap.setMapStatus(MapStatusUpdateFactory.newLatLng(point));
}
} catch (Exception e) {
// TODO: handle exception
}
}
};
这里我们拿到定位返回的LatLng,然后我们检索定位周边的POI
修改原先的search方法
private void search(String searchPoi) {
mPoiSearch = PoiSearch.newInstance();
/*mPoiSearch.searchInCity((new PoiCitySearchOption())
.city("深圳")
.keyword(searchPoi)
.pageNum(1)
.pageCapacity(5));*/
mPoiSearch.searchNearby(new PoiNearbySearchOption()
.keyword(searchPoi)
.location(point)
.radius(2000)
.pageNum(0)
.pageCapacity(5)
.sortType(PoiSortType.distance_from_near_to_far));
mPoiSearch.setOnGetPoiSearchResultListener(poiListener);
}
这样,就只能直接检索你所在位置的POI了