27、车载系统服务:SystemUI定制、StatusBar与NavigationBar、系统对话框、系统级通知

各位同学,今天我们来聊聊车载系统里最“露脸”的部分——SystemUI。说白了,就是用户每天开车时眼睛盯着的那块屏幕上的所有东西:顶部的状态栏、底部的导航栏、弹出来的对话框、还有那些飘出来的通知。这部分做得好不好,直接决定了用户觉得你这车机“专不专业”。

我个人习惯把SystemUI比作车机的“脸面”。你想想看,底层服务再牛,如果UI卡顿、布局混乱,用户第一印象就差了。我在项目里见过太多“功能强大但界面粗糙”的车机,最后都被用户吐槽。所以,这块值得我们花功夫。

27.1 SystemUI 整体架构

SystemUI在Android Automotive OS里是一个独立的APK,它不像手机版那样跟系统紧密耦合。车载版SystemUI需要更灵活,因为不同车型的屏幕尺寸、形状、分辨率差异太大了。

它的核心模块包括:

  • StatusBar:显示时间、信号、电量、蓝牙等状态图标。
  • NavigationBar:提供返回、主页、最近任务(以及空调、座椅等快捷控制)。
  • 系统对话框:比如音量调节、驾驶模式切换、警告弹窗。
  • 系统级通知:来自地图、音乐、电话等应用的通知。

嗯,这里要注意,车载SystemUI的定制通常不是改改图标颜色那么简单。很多车厂会要求完全重写布局,甚至把StatusBar和NavigationBar合并成一条“任务栏”。

核心原则:车载SystemUI必须保证驾驶安全。任何交互都不能让驾驶员分心超过2秒。所以,我们定制时,信息要精简,操作要直观。

SystemUI 核心模块架构 SystemUI APK StatusBar NavigationBar 系统对话框 系统级通知 快捷控制面板

27.2 StatusBar 定制实战

StatusBar在车载上通常比手机更简洁。手机上有电池百分比、双卡信号、通知图标一大堆,车上不需要这些。我建议只保留:时间、蓝牙连接状态、网络信号、以及一个“驾驶模式”指示器。

定制StatusBar,最直接的方式是修改SystemUI的布局文件。比如,在status_bar.xml里,你可以调整图标顺序、添加自定义View。

<!-- 车载StatusBar示例 -->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="@color/car_status_bar_bg">

    <!-- 左侧:时间 -->
    <TextView
        android:id="@+id/clock"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:textSize="16sp"
        android:textColor="@android:color/white"
        android:paddingStart="16dp"/>

    <!-- 右侧:状态图标区 -->
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="end"
        android:orientation="horizontal"
        android:paddingEnd="16dp">
        <!-- 蓝牙、信号、电量等图标 -->
    </LinearLayout>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>

我在项目中遇到过一个问题:某款车型的屏幕是圆形的,标准的StatusBar放上去很难看。后来我们干脆把StatusBar做成了半圆弧形,沿着屏幕边缘走。这需要重写onLayout方法,但效果确实好。

小技巧:车载StatusBar的高度建议不低于48dp,但也不超过64dp。太矮了手指不好点,太高了占内容区域。我一般用56dp,视觉上比较平衡。

27.3 NavigationBar 定制要点

NavigationBar在车上承担了更多职责。除了传统的三键(返回、主页、最近任务),很多车厂会把空调控制、座椅加热、音量旋钮也集成进来。这就不是简单的“隐藏或显示”了,而是需要动态添加按钮。

我建议的做法是:在NavigationBar里预留一个FlexboxLayout,然后通过配置动态加载按钮。这样不同车型只需要改配置文件,不用改代码。

// 动态添加快捷按钮示例
FlexboxLayout navContainer = findViewById(R.id.nav_buttons);
List<NavButton> buttons = getConfigButtons(); // 从配置文件读取
for (NavButton btn : buttons) {
    View buttonView = LayoutInflater.from(this)
        .inflate(R.layout.nav_button_item, navContainer, false);
    // 设置图标、点击事件
    navContainer.addView(buttonView);
}

嗯,这里有个坑。我曾经在某个项目里,把空调控制直接塞进NavigationBar,结果发现空调状态变化时,NavigationBar需要频繁刷新,导致导航动画掉帧。后来我们把空调状态抽成一个独立的LiveData,只在状态变化时更新对应的图标,问题就解决了。

注意:不要在NavigationBar里放太多按钮。根据NHTSA(美国国家公路交通安全管理局)的建议,驾驶中可交互的元素不超过7个。我一般控制在5个以内,包括系统三键。

27.4 系统对话框定制

车载系统对话框跟手机有很大不同。手机上的对话框可以很花哨,但车上必须简洁、大字体、高对比度。而且,很多对话框需要支持“驾驶模式”下的特殊交互——比如语音控制。

举个例子,音量调节对话框。手机上是竖条滑块,车上我建议做成横向的,而且显示当前音量等级(1-10)。这样驾驶员扫一眼就知道音量大小,不用仔细看。

<!-- 车载音量对话框布局 -->
<LinearLayout
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:padding="16dp"
    android:background="@drawable/car_dialog_bg">

    <ImageView android:src="@drawable/ic_volume" />
    <SeekBar
        android:id="@+id/volume_slider"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:max="10"
        android:progress="5"/>
    <TextView
        android:id="@+id/volume_level"
        android:text="5"
        android:textSize="18sp"
        android:textColor="@android:color/white"
        android:paddingStart="8dp"/>
</LinearLayout>

我习惯在对话框出现时,同时触发一个短暂的震动反馈(Haptic Feedback)。这样驾驶员即使不看屏幕,也知道有对话框弹出来了。但注意,震动不能太长,200毫秒以内就够了。

27.5 系统级通知定制

车载通知跟手机通知最大的区别是:不能打扰驾驶。手机可以响铃+震动+弹窗,车上只能“轻提醒”。我建议的做法是:通知只出现在StatusBar的左侧,以图标形式显示,点击后才展开详情。

另外,通知的优先级在车上需要重新定义。比如,导航提示的优先级应该高于音乐播放通知。我们可以通过修改NotificationChannel的importance来实现。

// 创建车载通知渠道示例
NotificationChannel carChannel = new NotificationChannel(
    "car_navigation",
    "导航提示",
    NotificationManager.IMPORTANCE_HIGH  // 高优先级,但不会弹出全屏
);
carChannel.setDescription("导航相关的转弯、测速提醒");
carChannel.setShowBadge(false);  // 不显示角标
notificationManager.createNotificationChannel(carChannel);

我曾经踩过一个坑:某款车机在收到多条通知后,StatusBar的通知图标重叠在一起,看不清。后来我们限制了StatusBar上最多显示3个通知图标,多余的通知折叠成一个“更多”图标。用户点击后展开列表。这个方案后来成了我们团队的标配。

避坑指南:车载通知的点击事件一定要慎重。不要在通知上放“确认”或“取消”按钮,因为驾驶员可能误触。我建议只放一个“查看详情”的intent,点击后打开对应的应用页面。

27.6 总结与个人经验

SystemUI定制,说白了就是在“安全”和“功能”之间找平衡。我做了这么多项目,最大的体会是:少即是多。用户开车时不需要花哨的动画,不需要复杂的设置。他们需要的是:一眼看清时间、信号、导航提示,随手能调音量、切歌、开空调。

如果你刚开始接触车载SystemUI,我建议你先从StatusBar和NavigationBar的布局改起。把默认的图标换成符合车机风格的,把间距调大,把字体加粗。这些改动虽然简单,但用户感知最明显。

最后,记住一点:车载系统不是手机的移植版。它是为驾驶场景量身定做的。你做的每一个UI决策,都要问自己一句:“这个功能在时速120公里时,驾驶员能安全操作吗?”


公众号:蓝海资料掘金营,微信deep3321