本页详细介绍了汽车应用库的不同功能,您可以使用这些功能实现精细导航应用的功能。
在清单中声明导航支持
导航应用需要在其 CarAppService
的 intent 过滤器中声明 androidx.car.app.category.NAVIGATION
汽车应用类别:
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
...
</application>
支持导航 intent
为了支持发送到应用的导航 intent(包括来自 Google 助理的语音查询导航 intent),应用需要在其 Session.onCreateScreen
和 Session.onNewIntent
中处理 CarContext.ACTION_NAVIGATE
intent。
如需详细了解该 intent 的格式,请参阅 CarContext.startCarApp
的文档。
访问导航模板
导航应用可以访问专为导航应用设计的以下模板。所有这些模板都会在地图背景上显示一个 surface,在有效导航期间还会显示精细导航路线。
NavigationTemplate
:在有效导航期间,还会显示可选的信息性消息和行程估计数据。MapTemplate
:在地图旁显示列表的紧凑版本(参见ListTemplate
)或窗格的紧凑版本(含有醒目操作的详细信息,参见PaneTemplate
)。PlaceListNavigationTemplate
:还会显示地点列表,可在地图上绘制对应的地点标记。RoutePreviewNavigationTemplate
:还会显示路线列表,可以选择其中一个路线并在地图上突出显示。
如需详细了解如何使用这些模板设计导航应用的界面,请参阅 Android for Cars 应用库设计准则。
为了能够访问导航模板,应用需要在其 AndroidManifest.xml
中声明 androidx.car.app.NAVIGATION_TEMPLATES
权限:
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
绘制地图
导航应用可以访问 Surface
,以在相关模板上绘制地图。
然后,可以通过将 SurfaceCallback
实例设置为 AppManager
汽车服务来访问 SurfaceContainer
对象:
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
当 SurfaceContainer
可用时,SurfaceCallback
会提供一个回调;当 Surface
的属性发生更改时,它还会提供其他回调。
为了能够访问 Surface,应用需要在其 AndroidManifest.xml
文件中声明 androidx.car.app.ACCESS_SURFACE
权限:
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>
地图的可见区域
主机可以在地图上绘制模板的界面元素。主机将通过调用 SurfaceCallback.onVisibleAreaChanged
来告知保证不被遮挡且完全对用户可见的区域。此外,为了最大限度地减少更改次数,主机还会使用根据当前模板将会始终可见的最小矩形来调用 SurfaceCallback.onStableAreaChanged
方法。
例如,如果导航应用使用的是顶部带有操作栏的 NavigationTemplate
,当用户有一段时间没有与屏幕交互时,该操作栏可能会自动隐藏,以便为地图腾出更多空间。在这种情况下,将使用相同的矩形对 onStableAreaChanged
和 onVisibleAreaChanged
进行回调。当操作栏处于隐藏状态时,仅使用较大的区域调用 onVisibleAreaChanged
。如果用户与屏幕交互,则同样仅使用第一个矩形调用 onVisibleAreaChanged
。
支持深色模式
当主机确定条件允许时,导航应用必须使用适当的深色将地图重新绘制到 Surface
实例上,如 Android 汽车应用质量中所述。
为了决定是否应绘制深色地图,您可以使用 CarContext.isDarkMode
方法。每当深色模式状态发生变化时,您都会收到对 Session.onCarConfigurationChanged
的调用。
传递导航元数据
导航应用必须就额外的导航元数据与主机通信。主机利用这些信息向车机提供信息,并防止导航应用在共享资源上发生冲突。
导航元数据通过可从 CarContext
访问的 NavigationManager
汽车服务提供:
Kotlin
val navigationManager = carContext.getCarService(NavigationManager::class.java)
Java
NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
启动、结束和停止导航
主机需要了解导航的当前状态,才能管理多个导航应用、路线通知和车辆仪表板数据。当用户开始导航时,应用调用 NavigationManager.navigationStarted
。同样,当导航结束时,例如当用户到达目的地或用户取消导航时,应用调用 NavigationManager.navigationEnded
。
只有在用户完成导航时才能调用 NavigationManager.navigationEnded
。例如,如果您需要在行程中间重新计算路线,请改用 Trip.Builder.setLoading(true)
。
有时,主机需要应用停止导航,并将在应用通过 NavigationManager.setNavigationManagerCallback
提供的 NavigationManagerCallback
对象中调用 onStopNavigation
。然后,应用必须停止在仪表板屏幕、导航通知和语音导航中发出下一个转弯的信息。
更新行程信息
在有效导航期间,调用 NavigationManager.updateTrip
。此调用中提供的信息可用于车辆的仪表板和平视显示仪。并非所有信息都可以显示给用户,具体取决于驾驶的特定车辆。例如,桌面车机 (DHU) 会显示添加到 Trip
的 Step
,但不会显示 Destination
信息。
绘制到仪表板显示屏
为了提供最沉浸式的用户体验,您可能不仅仅需要在车辆仪表板显示屏上显示基本元数据。从 Car App API 级别 6 开始,导航应用可以选择直接在仪表板显示屏(在支持的车辆中)呈现自己的内容,但存在以下限制:
- Cluster Display API 不支持输入控件
- 仪表板显示屏应仅显示地图图块。可以选择在这些图块上显示有效的路线导航。
- Cluster Display API 仅支持使用
NavigationTemplate
- 与主显示屏不同,仪表板显示屏可能不会以一致的方式显示所有
NavigationTemplate
界面元素,例如精细导航说明、预计到达时间卡片和操作。地图图块是唯一一致显示的界面元素。
- 与主显示屏不同,仪表板显示屏可能不会以一致的方式显示所有
声明支持仪表板
如需让托管应用知道您的应用支持在仪表板显示屏上呈现,您必须向 CarAppService
的 <intent-filter>
添加 androidx.car.app.category.FEATURE_CLUSTER
<category>
元素,如以下代码段所示:
<application> ... <service ... android:name=".MyNavigationCarAppService" android:exported="true"> <intent-filter> <action android:name="androidx.car.app.CarAppService" /> <category android:name="androidx.car.app.category.NAVIGATION"/> <category android:name="androidx.car.app.category.FEATURE_CLUSTER"/> </intent-filter> </service> ... </application>
生命周期和状态管理
从 API 级别 6 开始,汽车应用生命周期流程保持不变,但现在 CarAppService::onCreateSession
接受类型为 SessionInfo
的参数,提供了关于正在被创建的 Session
的其他信息(即显示屏类型和一组受支持的模板)。
应用可以选择使用相同的 Session
类来处理仪表板显示屏和主显示屏,也可以创建特定于显示屏的 Sessions
以自定义每个显示屏上的行为(如以下代码段所示)。
Kotlin
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { MainDisplaySession() } }
Java
@Override @NonNull public Session onCreateSession(@NonNull SessionInfo sessionInfo) { if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) { return new ClusterSession(); } else { return new MainDisplaySession(); } }
无法保证仪表板何时提供或是否提供,并且仪表板 Session
也可能是唯一的 Session
(例如,当您的应用正在导航时,用户将主显示屏切换给另一个应用)。“标准”协议是指,应用仅在调用 NavigationManager::navigationStarted
后才会获得仪表板显示屏控制权。不过,有可能是在没有进行导航时向应用提供仪表板显示屏,也可能根本未提供仪表板显示屏。应用需要通过渲染应用的地图图块空闲状态来处理这些场景。
主机会为每个 Session
创建单独的 binder 和 CarContext
实例。这意味着,在使用 ScreenManager::push
或 Screen::invalidate
等方法时,只会影响从中调用它们的 Session
。如果需要跨 Session
通信(例如,使用广播、共享单例或其他方式),应用应在这些实例之间创建自己的通信通道。
测试仪表板支持实现
您可以在 Android Auto 和 Android Automotive OS 上测试您的实现。对于 Android Auto,可通过配置桌面车机来模拟辅助仪表板显示屏进行测试。对于 Android Automotive OS,API 级别 30 及更高级别的通用系统映像会模拟仪表板显示屏。
使用文本或图标自定义 TravelEstimate
如需使用文本和/或图标自定义行程估计数据,请使用 TravelEstimate.Builder
类的 setTripIcon
或 setTripText
方法。NavigationTemplate
可以使用 TravelEstimate
选择将文本和图标设置为显示在预计到达时间、剩余时间和剩余距离旁边或替代这些信息。

以下代码段使用 setTripIcon
和 setTripText
自定义行程估计数据:
Kotlin
TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build()
Java
new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build();
提供精细导航通知
使用频繁更新的导航通知提供精细导航 (TBT) 说明。为了在车载显示屏中被视为导航通知,通知的构建器必须执行以下操作:
- 使用
NotificationCompat.Builder.setOngoing
方法将通知标记为持续性通知。 - 将通知的类别设置为
Notification.CATEGORY_NAVIGATION
。 - 使用
CarAppExtender
扩展通知。
导航通知将显示在车载显示屏底部的侧边栏 widget 中。如果通知的重要性级别设置为 IMPORTANCE_HIGH
,它也会显示为浮动通知 (HUN)。如果未使用 CarAppExtender.Builder.setImportance
方法设置重要性,将采用通知渠道的重要性。
应用可以在 CarAppExtender
中设置 PendingIntent
,以便在用户点按 HUN 或侧边栏 widget 时将其发送到应用。
如果调用 NotificationCompat.Builder.setOnlyAlertOnce
且将值设置为 true
,则高重要性通知将只以 HUN 的形式提醒一次。
以下代码段展示了如何构建导航通知:
Kotlin
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), Intent(ACTION_OPEN_APP).setComponent( ComponentName(context, MyNotificationReceiver::class.java)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build()
Java
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( new CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), new Intent(ACTION_OPEN_APP).setComponent( new ComponentName(context, MyNotificationReceiver.class)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build();
随着距离的变化定期更新 TBT 通知,这样会更新侧边栏 widget,并且只将通知显示为 HUN。您可以使用 CarAppExtender.Builder.setImportance
设置通知的重要性,由此控制 HUN 行为。如果将重要性设置为 IMPORTANCE_HIGH
,便会显示 HUN。如果将重要性设置为任何其他值,则只会更新侧边栏 widget。
刷新 PlaceListNavigationTemplate 内容
您可以允许驾驶员在浏览使用 PlaceListNavigationTemplate
构建的地点列表时通过点按按钮来刷新内容。如要启用列表刷新功能,请实现 OnContentRefreshListener
接口的 onContentRefreshRequested
方法并使用 PlaceListNavigationTemplate.Builder.setOnContentRefreshListener
在模板上设置监听器。
以下代码段展示了如何在模板上设置监听器:
Kotlin
PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate() } .build()
Java
new PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener(() -> { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate(); }) .build();
仅当监听器具有值时,PlaceListNavigationTemplate
的页眉中才会显示刷新按钮。
当驾驶员点击刷新按钮时,系统会调用您的 OnContentRefreshListener
实现的 onContentRefreshRequested
方法。在 onContentRefreshRequested
中,调用 Screen.invalidate
方法。
主机随后回调应用的 Screen.onGetTemplate
方法,以检索包含已刷新内容的模板。如需详细了解如何刷新模板,请参阅刷新模板的内容。只要 onGetTemplate
返回的下一个模板属于同一类型,系统就会将其视为一次刷新,而不会将其计入模板配额。
提供音频指导
如需通过汽车扬声器播放导航指导,应用必须请求音频焦点。在 AudioFocusRequest
中,将用途设置为 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
。此外,将焦点获取设置为 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
。
模拟导航
为了在将应用提交到 Google Play 商店时验证其导航功能,应用必须实现 NavigationManagerCallback.onAutoDriveEnabled
回调。如果调用了此回调,当用户开始导航时,应用应模拟导航到选定的目的地。只要当前 Session
的生命周期达到 Lifecycle.Event.ON_DESTROY
状态,应用就可以退出此模式。
您可以通过从命令行执行以下命令来测试是否调用了 onAutoDriveEnabled
实现:
adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE
具体可见以下示例:
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
默认汽车导航应用
在 Android Auto 中,默认汽车导航应用为用户最近启动的导航应用。当用户通过助理调用导航命令或其他应用发送 intent 以开始导航时,默认应用会接收导航 intent。
允许用户与您的地图互动
您可以添加相应支持来允许用户与地图互动,例如让用户通过缩放和平移查看地图的不同部分。每个模板都有不同的最低 Car App API 级别要求。如需了解您要实现的模板所适用的最低级别,请参阅下表。
模板 | 从哪个 Car App API 级别起支持相应互动功能 |
---|---|
NavigationTemplate | 2 |
PlaceListNavigationTemplate | 4 |
RoutePreviewNavigationTemplate | 4 |
MapTemplate | 5 |
SurfaceCallback 方法
SurfaceCallback
接口提供了多种回调方法,让您可以向使用 NavigationTemplate
、PlaceListNavigationTemplate
、RoutePreviewNavigationTemplate
或 MapTemplate
模板构建的地图添加互动支持:onClick
、onScroll
、onScale
和 onFling
。如需了解这些回调如何与用户互动关联,请参阅下表。
互动 | SurfaceCallback 方法 |
从哪个 Car App API 级别开始支持 |
---|---|---|
点按 | onClick |
5 |
双指张合即可缩放 | onScale |
2 |
单点触控拖动 | onScroll |
2 |
单点触控快速滑动 | onFling |
2 |
点按两次 | onScale (缩放比例由模板主机决定) |
2 |
平移模式下的旋转轻推 | onScroll (距离系数由模板主机决定) |
2 |
地图操作栏
NavigationTemplate
、PlaceListNavigationTemplate
、RoutePreviewNavigationTemplate
和 MapTemplate
可以为地图相关操作(例如放大和缩小、重新居中、显示罗盘或您选择显示的其他操作)提供地图操作栏。地图操作栏最多可包含四个仅显示图标的按钮,这些按钮可在不影响任务深度的情况下刷新。它在空闲状态下会隐藏,在活跃状态下则重新出现。
如需接收地图互动回调,您必须在地图操作栏中添加一个 Action.PAN
按钮。当用户按平移按钮时,主机会进入平移模式,具体如下一部分所述。
如果应用的地图操作栏中没有 Action.PAN
按钮,您将无法从 SurfaceCallback
方法接收用户输入,并且主机会退出先前启用的任何平移模式。
触摸屏上不会显示平移按钮。
平移模式
在平移模式下,模板主机会将来自非触控输入设备(例如旋控器和触控板)的用户输入在转换后传递给相应的 SurfaceCallback
方法。系统会使用 NavigationTemplate.Builder
中的 setPanModeListener
方法响应用户操作来进入或退出平移模式。当用户处于平移模式时,主机可以隐藏模板中的其他界面组件。
稳定区域
稳定区域会在状态于空闲和活跃之间切换时更新。根据稳定区域的大小来绘制速度、速度限制或道路警告等与驾驶相关的信息,以便地图操作栏不会遮蔽地图上的重要信息。
显示上下文导航提醒
Alert
可向驾驶员显示重要信息和可选操作,而无需离开导航屏幕的上下文。为了给驾驶员提供最佳体验,Alert
应在 NavigationTemplate
内运行,以免阻挡导航路线信息并尽量避免让驾驶员分心。
Alert
仅在 NavigationTemplate
中适用。如需通知 NavigationTemplate
之外的用户,请考虑使用浮动通知 (HUN),具体如显示通知中所述。
例如,使用 Alert
执行以下操作:
- 告知驾驶员与当前导航相关的新动态,例如路况信息变化。
- 向驾驶员询问与当前导航相关的新动态,例如是否存在移动测速装置。
- 就即将开始的任务提出建议,询问驾驶员是否愿意接受,例如驾驶员是否愿意顺路接人。
基本形式的 Alert
由标题和 Alert
时长组成。时长以进度条表示。您还可以选择添加副标题、图标和最多两个 Action
。

Alert
显示后,如果驾驶员互动会导致离开 NavigationTemplate
,相应信息不会转移到其他模板。它会一直保留在原始 NavigationTemplate
中,直到 Alert
超时、用户执行操作或应用关闭 Alert
。
创建提醒
使用 Alert.Builder
创建 Alert
实例:
Kotlin
Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build()
Java
new Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build();
如果您想监听 Alert
取消或关闭的操作,请创建 AlertCallback
接口的实现。AlertCallback
调用路径如下:
如果
Alert
超时,主机会使用AlertCallback.REASON_TIMEOUT
值调用AlertCallback.onCancel
方法。然后调用AlertCallback.onDismiss
方法。如果驾驶员点击某个操作按钮,主机会依次调用
Action.OnClickListener
和AlertCallback.onDismiss
。如果
Alert
不受支持,主机会使用AlertCallback.REASON_NOT_SUPPORTED
值调用AlertCallback.onCancel
。主机不会调用AlertCallback.onDismiss
,因为未显示Alert
。
配置提醒时长
选择符合应用需求的 Alert
时长。导航 Alert
的建议时长为 10 秒。如需了解详情,请参阅 Android for Cars 应用库设计准则。
显示提醒
如需显示 Alert
,请通过应用的 CarContext
调用可用的 AppManager.showAlert
。
// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
- 调用
showAlert
时,如果其Alert
的alertId
与当前所显示Alert
的 ID 相同,则系统不会执行任何操作。Alert
不会更新。如需更新Alert
,您必须使用新的alertId
重新创建它。 - 调用
showAlert
时,如果其Alert
的alertId
与当前显示的Alert
不同,则系统会关闭当前显示的Alert
。
关闭提醒
虽然 Alert
会因超时或驾驶员互动而自动关闭,但您也可以手动关闭 Alert
,例如当其信息已过时的时候。如需关闭 Alert
,请使用 Alert
的 alertId
调用 dismissAlert
方法。
// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())
调用 dismissAlert
时,如果其 alertId
与当前显示的 Alert
不匹配,则系统不会执行任何操作。系统不会抛出异常。