车载应用开发实战:基于 CarAppLibrary 构建仪表盘应用、媒体应用、空调控制应用
好,我们终于到了动手环节。前面讲了那么多框架、服务、生命周期,说白了都是为了这一刻——真正写一个能跑在车机上的应用。
我个人习惯,学一个新框架时,先拿三个典型应用练手:仪表盘、媒体播放、空调控制。为什么是这三个?因为它们覆盖了 CarAppLibrary 最核心的能力——分层布局、媒体模板、以及自定义控件。你想想看,车机上最常见的场景不就是看状态、听歌、调温度吗?
今天我就带你一个一个把它们实现出来。代码我会贴关键部分,但更重要的是思路——为什么这么写,以及我在项目中踩过的坑。
6.1 仪表盘应用:用 InstrumentClusterTemplate 展示行车数据
仪表盘应用,说白了就是给司机看车速、转速、续航这些关键信息。在 CarAppLibrary 里,Google 专门提供了一个模板叫 InstrumentClusterTemplate,它专为仪表盘场景设计,支持左右分栏、上下分栏,还能嵌入自定义布局。
核心要点:仪表盘应用必须使用 CarAppService 作为入口,而不是普通的 Service。这是很多新手容易忽略的地方。
先看代码结构。我习惯把仪表盘应用拆成三层:
- Screen 层:定义每个页面的布局和逻辑
- Service 层:管理生命周期和模板切换
- Manager 层:处理车辆数据源(比如 CAN 总线数据)
下面是一个最简单的仪表盘 Screen 实现:
public class InstrumentClusterScreen extends Screen {
private InstrumentClusterTemplate mTemplate;
public InstrumentClusterScreen(@NonNull CarContext carContext) {
super(carContext);
mTemplate = new InstrumentClusterTemplate.Builder()
.setTitle("行车数据")
.setSpeed(0) // 初始速度
.setRpm(0) // 初始转速
.setFuelLevel(50) // 油量百分比
.build();
}
@NonNull
@Override
public Template onGetTemplate() {
return mTemplate;
}
// 模拟数据更新
public void updateSpeed(int speed) {
mTemplate = new InstrumentClusterTemplate.Builder(mTemplate)
.setSpeed(speed)
.build();
invalidate(); // 通知模板刷新
}
}
嗯,这里要注意:invalidate() 方法必须调用,否则界面不会更新。我曾经在项目里调试了半天,发现数据明明变了但仪表盘不动,最后发现就是忘了调这个方法。
小技巧:如果你需要显示自定义的仪表盘元素(比如电池图标、故障灯),可以用 setCustomLayout() 传入一个 CarText 或 CarIcon 的组合。但注意,自定义布局的刷新频率不要超过 30fps,否则会卡顿。
6.2 媒体应用:用 MediaTemplate 实现音乐播放与控制
媒体应用是车载场景里使用频率最高的应用之一。CarAppLibrary 提供了 MediaTemplate,它内置了播放/暂停、上一首/下一首、进度条等控件,你只需要绑定数据源即可。
我建议你把媒体应用拆成两部分:
- 媒体浏览器(MediaBrowser):负责扫描和展示本地/在线音乐列表
- 媒体控制器(MediaController):负责播放、暂停、切歌等操作
下面是一个使用 MediaTemplate 的典型实现:
public class MediaScreen extends Screen {
private MediaTemplate mTemplate;
private List<MediaItem> mPlaylist;
public MediaScreen(@NonNull CarContext carContext) {
super(carContext);
mPlaylist = loadPlaylist(); // 从本地或网络加载歌单
mTemplate = new MediaTemplate.Builder()
.setTitle("我的音乐")
.setPlaylist(mPlaylist)
.setOnItemClickListener((item) -> {
// 点击播放
playMedia(item);
})
.build();
}
@NonNull
@Override
public Template onGetTemplate() {
return mTemplate;
}
private void playMedia(MediaItem item) {
// 实际项目中这里会调用 MediaSession 的 play() 方法
getCarContext().getCarService(MediaSession.class)
.play(item.getMediaId());
}
}
避坑指南:我曾经在项目里直接把整个歌单塞进 MediaTemplate,结果列表超过 200 首时,界面加载慢得像蜗牛。后来发现 MediaTemplate 内部用的是 RecyclerView,但数据绑定是同步的。解决方案是分页加载,每次只传 50 首,配合 setOnScrollListener 实现懒加载。
另外,媒体应用一定要处理好音频焦点。你想想看,如果用户在听歌时突然来了导航语音,两个声音同时播放,那体验得多糟糕。CarAppLibrary 的 AudioManager 已经帮你封装好了焦点请求,你只需要在播放前调用 requestAudioFocus() 即可。
6.3 空调控制应用:用 PaneTemplate 构建自定义控制面板
空调控制应用是展示 CarAppLibrary 灵活性的最佳例子。它不像仪表盘和媒体那样有现成的模板,而是需要你使用 PaneTemplate 配合自定义布局来实现。
PaneTemplate 允许你在一个页面里放置多个面板(Pane),每个面板可以包含按钮、滑块、开关等控件。我习惯把空调控制面板分成三个区域:
| 区域 | 功能 | 推荐控件 |
|---|---|---|
| 左侧面板 | 温度调节(驾驶位/副驾驶位) | CarText + 加减按钮 |
| 中间面板 | 风量、风向、A/C 开关 | Slider + ToggleButton |
| 右侧面板 | 座椅加热、通风、自动模式 | 图标按钮 + 状态指示 |
下面是一个使用 PaneTemplate 的示例:
public class ClimateControlScreen extends Screen {
private PaneTemplate mTemplate;
public ClimateControlScreen(@NonNull CarContext carContext) {
super(carContext);
// 创建左侧面板:温度控制
Pane leftPane = new Pane.Builder()
.setTitle("驾驶位温度")
.addAction(new Action.Builder()
.setTitle("降温")
.setOnClickListener(() -> decreaseTemp())
.build())
.addAction(new Action.Builder()
.setTitle("升温")
.setOnClickListener(() -> increaseTemp())
.build())
.build();
// 创建中间面板:风量控制
Pane centerPane = new Pane.Builder()
.setTitle("风量")
.addAction(new Action.Builder()
.setTitle("减小")
.setOnClickListener(() -> decreaseFanSpeed())
.build())
.addAction(new Action.Builder()
.setTitle("增大")
.setOnClickListener(() -> increaseFanSpeed())
.build())
.build();
mTemplate = new PaneTemplate.Builder()
.setTitle("空调控制")
.addPane(leftPane)
.addPane(centerPane)
.build();
}
@NonNull
@Override
public Template onGetTemplate() {
return mTemplate;
}
}
个人经验:空调控制应用里,我最头疼的是状态同步。比如用户通过物理按键调了温度,应用界面也得跟着变。解决方案是在 CarAppService 里注册一个 CarPropertyManager 监听器,当车辆 HVAC 属性变化时,主动调用 invalidate() 刷新模板。
6.4 三个应用的架构对比与选择建议
写到这里,你可能已经发现了:三个应用虽然都用 CarAppLibrary,但架构侧重点完全不同。我画了一张图帮你理清思路:
从图中你可以看到,三个应用的复杂度是递增的。仪表盘最简单,因为模板固定、数据源单一;媒体应用中等,需要处理列表和音频焦点;空调控制最复杂,因为涉及多面板布局和车辆属性同步。
我个人建议,如果你是第一次接触 CarAppLibrary,先从仪表盘应用入手,跑通整个流程后再挑战媒体和空调。我在带团队时也是这个顺序,新人一般两周就能上手。
最后说一句:CarAppLibrary 的模板化设计,说白了就是 Google 帮你把车机上 80% 的常见场景都封装好了。你不需要从零开始画界面,只需要关注业务逻辑和数据源。但剩下的 20% 自定义场景(比如特殊的 HMI 需求),就需要你深入理解 Screen 和 Template 的生命周期了。这部分我们下一章会详细讲。
公众号:蓝海资料掘金营,微信 deep3321