16、设置应用:车载设置界面定制、系统设置项扩展、多用户设置

车载系统的设置应用,说实话,跟手机上的设置完全是两码事。手机设置你做得再花哨,用户顶多点几下。车载设置不一样——它直接关系到驾驶安全、用户体验,还有整车厂的品牌调性。我参与过好几个车厂的项目,每次在设置这块都要反复打磨,因为这里是最容易暴露「这车机是公版方案」的地方。

今天我们就来聊聊,怎么把 Android Automotive OS 的设置应用,做出真正的「车规级」体验。

16.1 车载设置界面的定制思路

Android Automotive OS 自带的 Settings 应用,其实跟手机版同源。但你不能直接拿来用。为什么?因为车载场景下,用户的操作路径、关注点、甚至字体大小都不一样。

我个人习惯的做法是:先梳理出车载特有的设置分类,再基于 AOSP 的 Settings 框架做增量定制。

16.1.1 界面层级重构

车载设置通常分为三个层级:

  • 一级页面:驾驶相关、车辆控制、系统设置、账户与隐私
  • 二级页面:具体功能开关,比如「自动落锁」「倒车影像延迟」
  • 三级页面:详细配置,比如「Wi-Fi 高级选项」「显示校准」

嗯,这里要注意:车载设置尽量不要超过三级。驾驶员在行驶中操作,层级越深越危险。

核心原则:车载设置界面,所有可操作项必须满足「3秒触达」原则。也就是从首页到完成设置,点击次数不超过3次。

16.1.2 自定义 Preference 组件

AOSP 的 Settings 基于 PreferenceFragment 构建。车载定制时,我们通常需要自定义 Preference 样式:

// 自定义一个带车辆图标的 SwitchPreference
public class VehicleSwitchPreference extends SwitchPreference {
    public VehicleSwitchPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayoutResource(R.layout.vehicle_preference_switch);
        setIcon(Resources.getSystem().getDrawable(
            android.R.drawable.ic_menu_car, context.getTheme()));
    }

    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        // 调整字体大小,适配车载大屏
        TextView title = view.findViewById(android.R.id.title);
        if (title != null) {
            title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
        }
    }
}

我在项目中遇到过一个问题:车机屏幕分辨率差异巨大,从 720p 到 4K 都有。如果直接用 dp 写死,低分辨率下文字会挤在一起。后来我们统一用 sp + 最小宽度限定符来解决。

16.2 系统设置项扩展

车载系统需要很多手机没有的设置项。比如「驾驶模式」「充电策略」「空调联动」等。这些设置项不能随便加,需要遵循 Android 的 Settings.System 或 Settings.Global 机制。

16.2.1 添加自定义设置项

扩展设置项的标准做法是:

  1. 在 SettingsProvider 中注册新的 URI
  2. 定义对应的常量类
  3. 在 Settings 应用中添加 UI 入口

举个例子,添加一个「自动折叠后视镜」的设置:

// 1. 在 SettingsProvider 的 database 中添加
public static final String KEY_AUTO_FOLD_MIRROR = "auto_fold_mirror";

// 2. 定义常量类
public final class VehicleSettings {
    public static final String AUTO_FOLD_MIRROR = "auto_fold_mirror";
    
    public static boolean isAutoFoldMirror(Context context) {
        return Settings.System.getInt(context.getContentResolver(),
            AUTO_FOLD_MIRROR, 1) == 1;
    }
}

// 3. 在 Settings 应用中添加 Preference
public class VehicleSettingsFragment extends PreferenceFragment {
    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.vehicle_settings, rootKey);
        
        SwitchPreference mirrorPref = findPreference("auto_fold_mirror");
        mirrorPref.setOnPreferenceChangeListener((pref, newValue) -> {
            boolean enabled = (Boolean) newValue;
            // 调用车辆 HAL 接口执行操作
            VehicleHalClient.setProperty(
                VehicleProperty.MIRROR_FOLD, enabled);
            return true;
        });
    }
}

注意:千万不要把车辆控制逻辑直接写在 Settings 应用里。我曾经见过一个项目,把空调控制代码直接写在 Preference 的 onClick 中,结果导致设置页面 ANR。正确的做法是通过 Vehicle HAL 或 CarService 代理调用。

16.2.2 设置项的权限控制

有些设置项需要特殊权限,比如「修改驾驶模式」「重置车辆数据」。Android Automotive OS 提供了 android.car.permission.CAR_CONTROL_AUDIO_SETTINGS 等系统权限。你需要:

  • 在 AndroidManifest.xml 中声明权限
  • 在 Settings 中检查权限,没有权限时隐藏或禁用
  • 对于敏感设置,增加二次确认弹窗

16.3 多用户设置

车载多用户跟手机多用户完全是两码事。手机多用户是「不同的人用同一台手机」,车载多用户是「不同的人开同一辆车,但车是共享的」。所以车载多用户的核心是:用户配置与车辆状态分离

16.3.1 用户配置的存储策略

每个用户的设置应该存储在 /data/system/users/<userId>/ 下。但车辆的一些物理设置(比如座椅位置、方向盘角度)不能跟着用户走——因为换驾驶员时,车还在路上跑。

我建议的做法是:

  • 用户专属设置:壁纸、音效偏好、导航历史 → 存储在用户目录
  • 车辆共享设置:自动大灯、雨量感应、驾驶模式 → 存储在全局 Settings
  • 记忆类设置:座椅位置、后视镜角度 → 存储在用户目录,但需要与车辆 ECU 同步

16.3.2 多用户切换流程

车载多用户切换不能像手机那样直接锁屏。想象一下:驾驶员正在高速上开车,突然切换用户导致设置重置——这太危险了。

正确的流程应该是:

  1. 车辆熄火后,系统记录当前用户状态
  2. 下次点火时,检测钥匙或手机蓝牙,自动匹配用户
  3. 如果匹配失败,显示用户选择界面(仅限停车时)
  4. 切换用户时,逐步恢复设置(先恢复安全相关,再恢复娱乐相关)

经验之谈:我曾经在某个项目中,把用户切换做成了「一键切换」,结果测试时发现切换过程中座椅突然移动,差点夹到人。后来我们改成了「渐进式恢复」——先恢复后视镜和方向盘,再恢复座椅,每个步骤间隔 500ms,并且有物理防夹检测。

16.3.3 多用户下的设置界面适配

设置界面需要区分「当前用户可修改」和「仅管理员可修改」。比如:

设置项 普通用户 管理员用户
个人壁纸 可修改 可修改
驾驶模式 可修改 可修改
车辆锁定策略 只读 可修改
恢复出厂设置 隐藏 可修改

实现时,可以在 Preference 的 onPrepare 方法中动态控制可见性和可用性:

@Override
public void onPrepare() {
    UserManager um = (UserManager) getContext()
        .getSystemService(Context.USER_SERVICE);
    boolean isAdmin = um.isAdminUser();
    
    Preference resetPref = findPreference("factory_reset");
    if (resetPref != null) {
        resetPref.setVisible(isAdmin);
    }
}

16.4 知识体系总览

下面这张图,是我自己总结的车载设置应用的核心架构。你看一眼,基本就能明白各个模块之间的关系:

车载设置应用核心架构 Settings 入口 界面定制 PreferenceFragment 设置项扩展 SettingsProvider + VehicleHal 多用户设置 UserManager + 渐进恢复 层级重构 自定义组件 3秒触达 注册URI 权限控制 配置分离 渐进恢复 关键约束 安全优先 · 3秒触达 · 用户配置与车辆状态分离

这张图把车载设置应用拆成了三个核心模块。你开发的时候,可以照着这个结构去组织代码。每个模块之间通过 ContentProvider 和 CarService 通信,不要直接耦合。

16.5 避坑指南

最后,分享几个我踩过的坑:

  • 不要直接修改 AOSP Settings 的源码。我曾经为了省事,直接在 Settings 源码里加车辆控制代码,结果每次 Google 发安全补丁,合并代码都痛苦得要死。正确的做法是用 overlay 机制。
  • 多用户切换时,别忘了恢复音频焦点。用户切换后,如果音乐还在播放,新用户可能会被吓到。我们后来在切换流程中加了音频焦点释放和重新获取的逻辑。
  • 设置项的默认值要跟车辆硬件匹配。比如有的车没有座椅加热,那设置里就不要出现这个选项。我建议在 Settings 启动时,通过 VehicleHal 查询硬件能力,动态生成设置项。

车载设置应用,说白了就是「把手机那套东西,改造成适合车用的样子」。但就是这「改造」二字,里面全是细节。希望今天的内容能帮你少走一些弯路。


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