96、地图与导航:高德地图SDK、POI搜索、路线规划、导航组件
地图与导航,几乎是现代App的标配功能。不管是外卖、打车,还是社交、旅游,你总得让用户知道「我在哪」和「怎么去」。
高德地图SDK,是国内用得最广的地图方案之一。我这些年做过好几个地图相关的项目,从简单的定位展示,到复杂的实时导航、轨迹回放,踩过的坑真不少。今天这一讲,我把核心知识点串起来,帮你快速上手。
本章核心脉络:
高德SDK接入 → 地图展示 → POI搜索 → 路线规划 → 导航组件集成
1. 高德SDK接入:别小看这一步
接入SDK本身不复杂,但细节决定成败。我见过太多人卡在「地图一片灰」的问题上。
首先,去高德开放平台注册应用,拿到API Key。这一步要注意:Android的SHA1指纹必须和签名文件一致。调试用debug.keystore,发布用release,别搞混了。
⚠️ 我曾经遇到过: 开发时地图正常显示,打包上线后地图全灰。查了半天,发现是release签名没配置SHA1。嗯,这种低级错误,犯过一次就记住了。
在build.gradle里添加依赖:
dependencies {
implementation 'com.amap.api:3dmap:latest.integration'
implementation 'com.amap.api:search:latest.integration'
implementation 'com.amap.api:navi-3dmap:latest.integration'
}
然后在AndroidManifest.xml里配置Key:
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="你的API Key" />
别忘了申请定位权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2. 地图展示:让地图「动」起来
地图初始化很简单,在布局里放一个MapView:
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在Activity里:
public class MapActivity extends AppCompatActivity {
private MapView mapView;
private AMap aMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
mapView = findViewById(R.id.map);
mapView.onCreate(savedInstanceState);
aMap = mapView.getMap();
// 设置地图类型
aMap.setMapType(AMap.MAP_TYPE_NORMAL);
// 开启定位层
aMap.setMyLocationEnabled(true);
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}
💡 我个人习惯: 把MapView的生命周期方法全部写在基类里,避免子类遗漏。你想想看,如果忘了调onDestroy,内存泄漏是小事,地图卡顿才是真头疼。
3. POI搜索:让用户找到想去的地方
POI搜索,说白了就是「搜地点」。高德提供了关键词搜索和周边搜索两种方式。
先初始化搜索对象:
PoiSearch.Query query = new PoiSearch.Query("加油站", "", "北京");
query.setPageSize(20);
query.setPageNum(0);
PoiSearch poiSearch = new PoiSearch(this, query);
poiSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() {
@Override
public void onPoiSearched(PoiResult result, int rCode) {
if (rCode == 1000 && result != null) {
List<PoiItem> items = result.getPois();
// 把结果展示在地图上
for (PoiItem item : items) {
LatLonPoint point = item.getLatLonPoint();
// 添加Marker
}
}
}
@Override
public void onPoiItemSearched(PoiItem item, int rCode) {
// 单个POI详情
}
});
poiSearch.searchPOIAsyn();
周边搜索更实用,比如「我附近有什么好吃的」:
PoiSearch.Query query = new PoiSearch.Query("美食", "", "");
query.setPageSize(20);
PoiSearch poiSearch = new PoiSearch(this, query);
poiSearch.setBound(new PoiSearch.SearchBound(
new LatLonPoint(lat, lng), 1000)); // 半径1公里
poiSearch.searchPOIAsyn();
注意: POI搜索是异步的,回调在子线程。更新UI记得切回主线程。我习惯用Handler或者LiveData来处理。
4. 路线规划:从A到B的多种走法
路线规划支持驾车、步行、公交、骑行。我重点讲驾车路线,其他大同小异。
RouteSearch routeSearch = new RouteSearch(this);
routeSearch.setRouteSearchListener(new RouteSearch.OnRouteSearchListener() {
@Override
public void onDriveRouteSearched(DriveRouteResult result, int rCode) {
if (rCode == 1000 && result != null) {
List<DrivePath> paths = result.getPaths();
// 取第一条路线
DrivePath path = paths.get(0);
List<DriveStep> steps = path.getSteps();
// 绘制路线
for (DriveStep step : steps) {
List<LatLonPoint> points = step.getPolyline();
// 用Polyline画到地图上
}
}
}
@Override
public void onWalkRouteSearched(WalkRouteResult result, int rCode) { }
@Override
public void onBusRouteSearched(BusRouteResult result, int rCode) { }
@Override
public void onRideRouteSearched(RideRouteResult result, int rCode) { }
});
// 构建起终点
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(
new LatLonPoint(startLat, startLng),
new LatLonPoint(endLat, endLng)
);
// 驾车策略:避免拥堵
RouteSearch.DriveRouteQuery query = new RouteSearch.DriveRouteQuery(
fromAndTo, RouteSearch.DrivingDefault, null, null, "");
routeSearch.calculateDriveRouteAsyn(query);
💡 避坑指南: 路线规划的结果里,DrivePath 包含多个 DriveStep,每个Step又包含一段折线。绘制时要把所有Step的折线连起来,不然路线会断断续续。我曾经因为这个被测试小姐姐追着改bug……
5. 导航组件:一键开启导航
高德提供了现成的导航组件,说白了就是帮你把地图、路线、语音播报、转向提示都封装好了。你只需要调用一个Intent。
// 构建导航参数
AMapNaviParams naviParams = new AMapNaviParams(
new LatLonPoint(startLat, startLng), // 起点
new LatLonPoint(endLat, endLng), // 终点
AMapNaviPathType.DRIVING_DEFAULT, // 驾车模式
AMapCarType.DEFAULT // 车辆类型
);
// 启动导航界面
AMapNavi.getInstance(this).startNavi(naviParams);
如果你想自定义导航界面,高德也提供了 AMapNaviView 组件:
<com.amap.api.navi.AMapNaviView
android:id="@+id/navi_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
然后在Activity里:
AMapNaviView naviView = findViewById(R.id.navi_view);
naviView.onCreate(savedInstanceState);
naviView.setAMapNaviViewListener(new AMapNaviViewListener() {
@Override
public void onNaviSetting() {
// 用户点击设置按钮
}
@Override
public void onNaviCancel() {
// 用户取消导航
finish();
}
});
// 开始导航
AMapNavi.getInstance(this).calculateDriveRoute(
new LatLonPoint(startLat, startLng),
new LatLonPoint(endLat, endLng),
AMapNaviPathType.DRIVING_DEFAULT);
⚠️ 注意: 导航组件需要后台定位权限,否则导航过程中定位会断。另外,语音播报需要下载离线语音包,或者使用在线合成。我建议提前下载好,避免用户在路上等语音包下载。
6. 性能优化与常见问题
地图类App最怕什么?卡顿和内存泄漏。我总结几个要点:
- 地图复用: 如果多个页面需要地图,考虑用Fragment共享一个MapView,而不是每个页面都创建新的。
- Marker管理: 大量Marker时,用
MarkerOptions批量添加,不要一个个add。超过200个Marker建议用聚合。 - 定位频率: 导航时定位频率高,但普通展示场景下,每5秒定位一次就够了。省电也省流量。
- 内存泄漏: 地图SDK内部持有Activity引用,一定要在onDestroy里调用
mapView.onDestroy()和AMapNavi.destroy()。
最后说一句: 地图开发,调试起来比较麻烦。模拟器不支持定位,真机调试是必须的。我建议准备一台旧手机专门跑地图测试,省得天天插拔数据线。
好了,这一讲的内容就到这里。地图与导航的功能,说白了就是「定位 + 搜索 + 路线 + 导航」四个模块的组合。你只要把每个模块的API摸透,剩下的就是业务逻辑的拼装了。