19、地图与导航:集成第三方地图 SDK、导航栏与 HUD 显示、路线规划
车机上的地图导航,说实话,是用户每天都会盯着看的功能。你想想看,一个 Android Automotive OS 的车机,如果导航做不好,用户的第一反应就是“这车机不行”。我在几个量产项目里都踩过地图集成的坑,今天就把这些经验掰开揉碎了讲给你听。
19.1 第三方地图 SDK 的集成策略
Android Automotive OS 本身没有内置地图引擎。我们得选一个第三方 SDK。国内主流是高德、百度,海外是 Google Maps、TomTom。我个人习惯在项目初期就定好 SDK 版本,因为后期换 SDK 的成本极高——我见过一个团队因为没提前确认 API 兼容性,导致导航模块重写了三遍。
19.1.1 集成前的关键检查项
| 检查项 | 说明 | 我的建议 |
|---|---|---|
| API 密钥管理 | SDK 需要 API Key 才能使用 | 用 Android Keystore 加密存储,别硬编码 |
| 权限声明 | 定位、网络、存储等权限 | 在 Automotive 的 manifest 里单独声明 |
| SDK 大小 | 部分 SDK 超过 50MB | 建议用动态 feature 模块按需下载 |
| 合规要求 | 数据隐私、地图数据离线存储 | 提前和法务确认,尤其是海外市场 |
19.1.2 集成步骤(以高德地图为例)
其实步骤都差不多。核心就三步:引入依赖、初始化、显示地图。
// build.gradle (Module)
dependencies {
implementation 'com.amap.api:navi-3dmap:9.8.0'
implementation 'com.amap.api:search:9.8.0'
}
// Application 中初始化
public class CarApp extends Application {
@Override
public void onCreate() {
super.onCreate();
AMapNavi.setApiKey("your_api_key_here");
// 嗯,这里要注意:初始化最好放在主线程,但别阻塞太久
}
}
// 在 Fragment 或 Activity 中显示地图
public class MapFragment extends Fragment {
private MapView mapView;
private AMap aMap;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mapView = new MapView(getContext());
mapView.onCreate(savedInstanceState);
aMap = mapView.getMap();
// 设置地图类型为导航模式
aMap.setMapType(AMap.MAP_TYPE_NAVI);
return mapView;
}
}
CarAppDisplay 的 onStart() 和 onStop() 来控制 MapView 的可见性,避免后台浪费流量。
19.2 导航栏与 HUD 显示
车机上导航栏和 HUD(抬头显示)是两个完全不同的渲染层级。导航栏是给乘客看的,HUD 是给驾驶员看的。说白了,HUD 的信息要更精简、更聚焦。
19.2.1 导航栏的设计要点
导航栏通常位于屏幕左侧或底部。我个人习惯放在左侧,因为驾驶员视线从左到右扫视更自然。导航栏上需要显示:
- 当前道路名称:实时更新,不要延迟超过 1 秒
- 下一路口距离:用进度条或数字显示
- 转向箭头:用矢量图,不要用位图,否则缩放会糊
- 预计到达时间:结合实时路况计算
我在项目中遇到过一个问题:导航栏的更新频率太高,导致 UI 线程卡顿。后来我们用了 Choreographer 来同步帧率,只在垂直同步信号到来时才更新 UI,流畅度提升很明显。
19.2.2 HUD 显示的实现
HUD 显示在挡风玻璃上,所以它的背景必须是透明的。Android Automotive OS 提供了 CarHUDManager 来管理 HUD 窗口。注意,HUD 的显示内容不能太多,否则会干扰驾驶。
// 获取 HUD 管理器
CarHUDManager hudManager = (CarHUDManager) getSystemService(Context.CAR_HUD_SERVICE);
// 创建 HUD 显示内容
HudRender hudRender = new HudRender() {
@Override
public void onRender(Surface surface, int width, int height) {
Canvas canvas = surface.lockCanvas(null);
// 绘制简洁的导航信息
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setTextSize(48);
canvas.drawText("前方 200 米右转", 50, 100, paint);
// 绘制速度、车道线等
surface.unlockCanvasAndPost(canvas);
}
};
// 注册 HUD 显示
hudManager.registerHudRender(hudRender, new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
});
19.3 路线规划
路线规划是导航的核心。用户输入目的地后,我们要在几秒内给出多条路线。这里涉及两个关键点:算路策略和路线展示。
19.3.1 算路策略
第三方 SDK 通常提供多种算路模式:
- 最快路线:基于历史路况和限速
- 最短路线:距离优先,可能走小路
- 避开拥堵:实时路况加权
- 避开高速:适合货车或新手司机
我个人习惯在算路前先获取当前车辆的“驾驶模式”。如果是运动模式,优先推荐最快路线;如果是经济模式,优先推荐最短路线。这个逻辑在代码里很好实现:
// 获取驾驶模式(假设有 VehicleProperty)
int drivingMode = getVehicleProperty(VehicleProperty.DRIVING_MODE);
AMapNaviRouteOption option = new AMapNaviRouteOption();
if (drivingMode == 1) { // 运动模式
option.setRouteStrategy(AMapNaviRouteOption.ROUTE_STRATEGY_FASTEST);
} else if (drivingMode == 2) { // 经济模式
option.setRouteStrategy(AMapNaviRouteOption.ROUTE_STRATEGY_SHORTEST);
} else {
option.setRouteStrategy(AMapNaviRouteOption.ROUTE_STRATEGY_BALANCE);
}
// 发起算路
AMapNavi.getInstance(getContext()).calculateDriveRoute(
startPoint, endPoint, wayPoints, option, new AMapNaviCallback() {
@Override
public void onCalculateRouteSuccess(AMapNaviRoute[] routes) {
// 展示路线列表
showRouteList(routes);
}
});
19.3.2 路线展示与交互
算路成功后,我们通常会给用户展示 2-3 条路线。每条路线用不同的颜色绘制在地图上。用户点击某条路线后,才开始正式导航。
展示路线时,我建议用 PolylineOptions 来绘制,并设置不同的宽度和透明度:
// 绘制推荐路线(蓝色,粗线)
PolylineOptions blueLine = new PolylineOptions();
blueLine.addAll(route.getCoordinates());
blueLine.color(Color.BLUE);
blueLine.width(20);
aMap.addPolyline(blueLine);
// 绘制备选路线(灰色,细线)
PolylineOptions grayLine = new PolylineOptions();
grayLine.addAll(alternativeRoute.getCoordinates());
grayLine.color(Color.GRAY);
grayLine.width(10);
aMap.addPolyline(grayLine);
嗯,这里要注意:如果路线经过隧道,GPS 信号会丢失。我建议在路线规划时提前标记隧道区域,并在进入隧道前提示用户“即将进入隧道,导航将使用惯性推算”。这样用户体验会好很多。
19.4 知识体系总览
下面这张图是我自己整理的,把本章的核心逻辑串起来了。你一看就明白:地图 SDK 是底座,导航栏和 HUD 是输出,路线规划是大脑。
你看,整个模块分三层:底层是 SDK 集成,中间是导航栏和 HUD 两个输出通道,上层是路线规划逻辑。每一层都有各自的坑,但只要按我上面说的来,基本能平稳落地。
- 集成 SDK 前先检查 API 兼容性和权限,别等上线了才发现问题
- 导航栏用 Choreographer 同步帧率,HUD 控制刷新率在 15fps 以内
- 路线规划结合驾驶模式动态调整策略,算路结果用 ViewModel 传递
- 隧道场景提前标记,用惯性推算弥补 GPS 丢失
好了,地图与导航这块就聊到这儿。下一章我们会讲语音交互,到时候再细聊怎么把导航和语音指令串起来。