07 · 功能页面模块架构(settingsPage)
16 个 settingsPage 模块共享高度统一的约定。本文给出标准范式,新增功能页照此写。
一、统一页面组织模式
1.1 承载方式:PreferenceFragment(micarx.preference)
所有功能页统一用 PreferenceFragment 承载:Fragment 加载一个 XML(getPreferenceScreenResId()),XML 声明每个 <Preference> 及其 settings:controller="XxxController",Controller 负责数据刷新与点击。
继承链(最终都到 BaseXmlParserSettingsFragment):
BaseXmlParserSettingsFragment (核心, 1562 行)
├─ SettingsFragment → TopLevelSettingsFragment (一级页基类)
│ ├─ EnergyManagerFragment (充电)
│ ├─ LightsSettingsFragment (灯光)
│ ├─ UniversalSettingsFragment (系统)
│ └─ IotSettingsFragment (IoT)
└─ SecondLevelSettingsFragment (二级页基类)
1.2 一级 Fragment 四件套注解(标配)
@VoiceSearchProvider // 语音搜索可索引
@Module(SettingsConstant.TAG_CHARGE) // 路由模块归属
@RouterProvider(path = ENERGY_MANAGER_URI_PATH) // 外部深链路由
@MonitorFragment // 埋点
class EnergyManagerFragment : TopLevelSettingsFragment(), IPageRouteHandler {
override fun getPageAlias() = PageAlias.PAGE_ENERGY_MANAGER
override fun getPreferenceScreenResId() = R.xml.miauto_energy_manager_fragment
}1.3 双栏模型
首页左右双栏,右栏由 BaseRightFragment(实现 CarModelManager)承载(车型图、版本号)。一级 Fragment 可重写 getRightFragment() 指定右栏内容(如 UniversalSettingsFragment 返回 MiCarVersionRightFragment)。
二、标准目录结构(以 micarChargeSettings 为模板)
micarChargeSettings/
├── build.gradle # 统一模板
├── Android.bp # AOSP 构建(与 build.gradle 并存)
├── libs/ # 本地 jar
└── src/
├── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ ├── com/android/micar/settings/energy/ ← 业务包(新版包名)
│ │ │ ├── EnergyManagerFragment.kt (一级 Fragment)
│ │ │ ├── EnergyManagerRightFragment.kt (右栏)
│ │ │ ├── controller/ (PreferenceController)
│ │ │ ├── view/ (自定义 View/Dialog/Activity)
│ │ │ ├── viewmodel/
│ │ │ └── utils/
│ │ └── com/android/micar/settings/preference/ (自定义 Preference 控件)
│ └── res/
│ ├── xml/ ← 核心:PreferenceFragment 加载的菜单 XML
│ │ └── miauto_energy_manager_fragment.xml
│ ├── layout/ drawable/ values/ values-zh-rCN/ values-de/ ...
│ └── drawable-night/ values-night/
├── dcddif/ (dcd 差异: java/res)
├── xcddif/ (xcd 差异: java/res)
└── global/ (海外差异: java/res,约定存在)
包名两套并存(历史演进)
| 版本 | 包名 | 模块 |
|---|---|---|
| 新版 | com.android.micar.settings.<feature> | energy(charge)、iot、autopilot |
| 历史版 | com.android.car.settings.miauto.<feature> | display、volume、lights、driving、vehicle、safetyservice |
新功能走
com.android.micar.settings。
三、build.gradle 通用模板
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
flavorDimensions = ["miPlatform"]
productFlavors { dcd {}; xcd {}; global {} }
sourceSets {
dcd { java.srcDirs = ['src/main/java', 'src/dcddif/java']; res.srcDirs = ['src/main/res', 'src/dcddif/res'] }
xcd { java.srcDirs = ['src/main/java', 'src/xcddif/java']; res.srcDirs = ['src/main/res', 'src/xcddif/res'] }
global { java.srcDirs = ['src/main/java', 'src/xcddif/java', 'src/global/java']
res.srcDirs = ['src/main/res', 'src/xcddif/res', 'src/global/res'] }
}
}
dependencies {
implementation project(":base:settingsBaseUi") // Fragment/Controller 基类
api project(":base:settingsBaseLib") // 核心工具
implementation project(":base:settingsVehicleLib") // 车辆信号
kapt project(":settingsCommon:plugin:router:routerApt") // 路由表生成
kapt project(":settingsCommon:plugin:voice:voicesearchapt") // 语音索引生成
}例外:globalOnly 不依赖 settingsVehicleLib,不用 routerApt/voicesearchapt(海外隐私/位置/锁屏不走路控体系)。
四、数据来源(三种)
| 数据来源 | 典型模块 | 入口 |
|---|---|---|
| CarProperty(车辆信号) | charge / display / light / lock / driving / volume | SettingsCarPropertyManager |
| IoT 中心 SDK | micarIotSettings | IotClientManager(settingsBaseLib,监听设备卡片,不经 CarProperty) |
| 系统服务/配置 | settingsSystem | CarConfigManager、BuildNumberPreferenceController |
五、完整链路(充电「在途加热开关」为例)
[XML 声明] [Controller 层] [车辆信号层]
miauto_energy_manager_fragment.xml
<MiCarNewSwitchPreference EnergyHeatingSwitchController
settings:controller= extends BaseVehicleNewSwitchPrefController
"...EnergyHeatingSwitchController"> extends CarPropertyMgrPreferenceController<V>
android:key="@string/pk_..." implements CarPropertyManagerCallback
│ 持有
SettingsCarPropertyManager (settingsVehicleLib)
│
VehicleControlManager
│
android.car.CarPropertyManager ──Binder──► CarService ──► HAL
5.1 XML 绑定 Controller
<MiCarNewSwitchPreference
android:key="@string/pk_energy_heating_on_the_way"
android:title="@string/micar_energy_manager_heating_on_the_way_title"
settings:controller="com.android.micar.settings.energy.controller.EnergyHeatingSwitchController" />5.2 Controller 声明监听哪个信号
class EnergyHeatingSwitchController(...) : BaseVehicleNewSwitchPrefController(...) {
override fun getPropertyIdSet() = ArraySet<Int>().apply { add(HEATING_ON_THE_WAY) }
override fun isPropertyOpen(propVal: Int) = propVal in arrayOf(ON, ...ACTIVE)
}5.3 端到端 8 步
- 冷启动:
SettingsApplication→HomePageActivityReal加载TopLevelMenuFragment - 菜单渲染:
TopLevelMenuFragmentinflatemiauto_top_level_menu_fragment.xml - 点击菜单:「充电&能量」→ 按
android:fragment="...EnergyManagerFragment"实例化,加入右栏 - Fragment 初始化:父类解析 XML,反射创建 Controller,绑定 Preference
- Controller 订阅:
onCreateInternal()→registerPropertyCallback(getPropertyIdSet()) - 回流刷新:信号变化 →
onPropertyChanged→ 更新MiCarNewSwitchPreference - 用户改设置:
handlePreferenceChanged→setCarProperty(HEATING_ON_THE_WAY, ...)下发 - 外部深链(旁路):
carsettings://homepage/?subPage=energy_manage→ Router →EnergyManagerFragment.handleJump(uri)
六、车型差异的运行时控制
页面显隐还由 CarConfigManager(CCP 配置字)控制。例:
// EnergyManagerFragment.kt:75-96
override fun getPreferenceKeyResIdsToRemove(): List<Int> {
val remove = mutableListOf<Int>()
if (!CarConfigManager.isEceSocketType()) remove.add(R.string.pk_xxx)
if (CarConfigManager.getRoofRackPowerConfig() == 0) remove.add(R.string.pk_yyy)
return remove
}七、16 个模块速查表
| 维度 | 约定 |
|---|---|
| 一级页基类 | TopLevelSettingsFragment |
| 二级页基类 | SecondLevelSettingsFragment |
| 右栏基类 | BaseRightFragment |
| 车控 Controller 基类 | BaseVehicleNewSwitchPrefController / BaseVehicleTabPreferenceController 等 |
| 页面项注册 | XML:<Preference settings:controller="X"/> |
| 菜单注册 | app/src/region/{cn,global}/res/xml/miauto_top_level_menu_fragment.xml 的 android:fragment |
| 外部深链 | @RouterProvider(path=...) + IPageRouteHandler,path 在 CarSettingsJump.kt |
| 别名常量 | PageAlias.kt |
| 车控数据 | SettingsCarPropertyManager |
| IoT 数据 | IotClientManager(不经 CarProperty) |
| 车型差异 | dcddif/ / xcddif/ / global/ sourceSet + CarConfigManager 运行时 |
| 海外专属 | globalOnly 模块(仅 globalImplementation) |
| build.gradle | 三 flavor + BaseUi/BaseLib/VehicleLib + routerApt/voicesearchapt kapt |
八、新增功能模块 checklist
- 通常复用现有
settingsPage/*,不新建模块。 - 确需新建:
settings.gradle加include+SettingsApplication.@Modules加模块名(漏了路由静默失效)。 - 新建 Fragment:继承
TopLevelSettingsFragment或SettingsFragment,实现IPageRouteHandler。 - 加四件套注解。
- 加常量:
PageAlias.kt加PAGE_XXX;CarSettingsJump.kt加XXX_URI_PATH。 - 新建 XML
res/xml/xxx_fragment.xml。 - 新建 Controller(车控继承
BaseVehicleNewSwitchPrefController,构造器 4 参)。 - 多语言:
values/+values-zh-rCN/+values-en-rGB/三处。 - 父页面加入口(XML Preference + 入口 Controller)。
./gradlew checkStyleCode自查 → 提交。
完整 16 条 checklist + 12 条踩坑清单见现有
docs/08-开发实战-新增设置页.md。