01 · 功能与页面结构(新手入门)
读完本篇你应该能:讲清「能量回收强度」是什么;在代码里找到三处滑块的挂载点; 说清它什么时候可见、什么时候置灰、在哪些车型上存在。
代码基线:
fuben23_micarsettings/MiCarSettings@rls-xcd-u-mp-26-v7.0(HEADe8b5a7ba5= 修复 V3/PS4 内容;修复前基底f63fe1cdc)。 仓库/home/zbc/car/fuben23_micarsettings/MiCarSettings。 检索纪律:controller 含大量 Kotlin,grep 必须同时覆盖 .java 和 .kt。
1. 功能定位
「能量回收强度」位于 设置 → 驾驶偏好,控制车辆松加速踏板后的回收拖拽力度:
- 预设档:一排 Tab —— 柔和 / 适中 / 标准 / 自定义
(
BrakingStrengthTabPreferenceController.buildTabData(),rls :68-83) - 自定义:选中「自定义」Tab 后,下方出现一条滑块,0~100 任意调节, 数值实时下发车控属性
预设档与自定义值的关系(中文文案实证
settingsPage/micarDrivingSettings/src/main/res/values-zh-rCN/strings.xml:102-110):
| Tab | 文案 key | 写入 REGENERATION_MODE 的值 | 描述文案(strings.xml) |
|---|---|---|---|
| 柔和 | ..._braking_strength_soft | SOFT = 1 | 「回收强度为 1%」(:107) |
| 适中 | ..._braking_strength_moderate | MODERATE = 4 | 「回收强度为 35%」(:108) |
| 标准 | ..._braking_strength_standard | STANDARD = 2 | 「回收强度为 100%」(:109) |
| 自定义 | ..._braking_strength_advanced | ADVANCED = 3 | 「自由设置能量回收等级(0 挡下请踩制动踏板以减速或刹停)」(:110) |
枚举值 javap 实证(
vehicle.support-0.11.58-SNAPSHOT.jar的mi.car.config.Energy$RegenerationMode): LOW/SOFT=1、MEDIUM/STANDARD=2、HIGH/ADVANCED=3、AUTO/MODERATE=4。 ⚠️ 注意 Tab 顺序与枚举值并不单调:适中=4、标准=2,分析日志时别按 UI 顺序猜值。 预设档描述文案(:107-110)在界面上的具体展示位置待验证(待读 Tab controller 的 summary 逻辑)。
它不是什么
- ❌ 滑块本身不是「档位开关」——选预设档写
REGENERATION_MODE,拖自定义滑块写ENERGY_REGENERATION_LEVEL,是两个不同的 propId(见 §4) - ❌ App 不做任何回收控制决策——它只把 0~100 的整数值同步下发给车控
(
setVehicleProperty同步方式,BaseVehicleProgressPrefController.java:233-237), 真正的扭矩响应在底层
手势语义需求裁决(2026-10-13 产品最终裁决,权威基准)
- GT 主题:只支持拖动;点击不生效(仅按压放大缩小动画,无值变化)→ 取整彻底关闭
- 普通主题(GT 关闭后的深浅色):拖动随手变化 + 点击定位到手位置 + 数值只能固定 整十;关键边界——贴 thumb ±1 点击也必须取整
- 决策理由:该边界直接否决了「净位移」判别方案(53→54→53 净位移 0 判点击)——
净位移会把「贴 thumb ±1 点击」误判成拖动而不取整,与「点击数值只能是整十」冲突;
因此选定
isIgnoreClick主题门控(按组件库点击能力区分主题),即修复 V3/PS4 已推内容 - 机制实现与判定表见 02-手势语义与取整机制 §3; 裁决过程与方案选型见 05 篇 §9.4
2. 页面拓扑:同一滑块,三个挂载点
三处页面用的是同一个控件类 MiCarIconSeekBarPreference 和同一组配置
(setValueRoundUp(true) + SLIDER_NORMAL),只是 controller 不同:
| # | 页面 | Fragment | XML(rls 行号) | Controller |
|---|---|---|---|---|
| ① | 驾驶偏好主页 | DrivingSettingsFragment(:64-65 绑 R.xml.miauto_driving_settings_fragment) | miauto_driving_settings_fragment.xml:111-113,key=pk_settings_driving_braking_strength | BrakingStrengthPreferenceController.java |
| ② | M1/M2 自定义页(Lux/Mann) | CustomSteeringModeFragment | micar_custom_steering_mode_fragment.xml:35-37,key=pk_settings_driving_custom_energy_recovery_strength | CustomEnergyRecoveryStrengthController.kt |
| ③ | 主驾自定义页 | MainCustomSteeringModeFragment | micar_main_custom_steering_mode_fragment.xml:13-15,key=pk_settings_driving_main_custom_energy_recovery_strength | MainCustomEnergyRecoveryStrengthController.kt |
三处 XML 节点都带 settings:supportVoiceAssist="false"(①:113、②:37、③:15)——
可见即可说对这三个滑块是关闭的(属性的解析与消费:
PreferenceXmlParser.java:183、VoiceSearchManager.java:471)。
主页 XML 里滑块上方紧邻的就是预设档 Tab
(miauto_driving_settings_fragment.xml:104-108,key=pk_settings_driving_braking_strength_level,
controller=BrakingStrengthTabPreferenceController),两者通过 BRAKING_MODE 信号联动:
选「自定义」→ Tab 写 REGENERATION_MODE=ADVANCED(3) → 信号回灌 → 滑块 setVisible(true)。
入口与二级页分发
「自定义模式」入口项的 controller 是 CustomSteeringModePreferenceController.java:
- 点击分发(:272-277):当前驾驶模式是
CUSTOM_1/CUSTOM_2→ M1/M2 二级页 (CustomSteeringModeFragment);当前模式命中主驾自定义配置 → 主驾自定义页 (MainCustomSteeringModeFragment);判定逻辑isMainDriveMode():300-312 - 入口可见性(:204-206):
CUSTOM_1 || CUSTOM_2 || (isLuxOrMann() && isMainDriveMode())—— 非 Lux/Mann 车型(如 GT)在主驾自定义模式下连入口都不显示
3. 三道门控(逐一代码实证)
滑块要「可见且可用」,依次过三道门:
门 1 · 可见性:档位必须是「自定义」
三 controller 同一语义:BRAKING_MODE == Energy.RegenerationMode.ADVANCED(3) 才可见。
- 主页:
BrakingStrengthPreferenceController.onPropertyManagerPrepared():199-202setVisible(getIntProperty(BRAKING_MODE) == Energy.RegenerationMode.ADVANCED); 另有开页缓存初值 :137-142 +DrivingFragmentViewModel.registerBrakingStrengthObserver实时跟随 :144-151 - M1 页:
CustomEnergyRecoveryStrengthController.kt缓存初值 :61-66、observer :72-76、onPropertyManagerPrepared:154-157 - 主自定义页:
MainCustomEnergyRecoveryStrengthController.ktobserver :67-71、onPropertyManagerPrepared:139-142;额外实现了getAvailabilityStatus():48-55, 非 ADVANCED 时返回CONDITIONALLY_UNAVAILABLE
门 2 · 可用性(置灰):BAN_REASON 必须为 0
- 三 controller 都在
getObservedPropertyIdSet()里额外观测ENERGY_REGENERATION_BAN_REASON(主页 java :211-215;M1 kt :100-104;主自定义 kt :97-101) - 判定:
isDependencyAvailable()→EnergyRecoveryModeHelper.isEnergyRecoveryAvailable(...)(EnergyRecoveryModeHelper.kt:29-32):信号值 == NO_INHIBIT(0) 且信号状态 Available 才可点击 - 置灰时仍允许触摸并弹原因 Toast:主页 :153-154
setAllowTouchWhenDisable(true)+setDisableClickListener,showDisableToast():158-191 按 reason 出文案 (战斗/湿滑/ADAS/除锈/陡坡缓降/防晕车/拖挂,javap 枚举:NO_INHIBIT=0、BATTLE=1、 WET/SNOW=2、ADAS=3、RSTREMV=4、HDC=5、AMS=6、TOW=7)
门 3 · 车型拓扑:Lux/Mann 才有自定义页的能量回收项
MiCarSettings.isCustomEnergyRecoveryEnabled()(base/settingsBaseLib/…/views/ MiCarSettings.java:1204-1206)直接return isLuxOrMann();isLuxOrMann():1214-1217 = 车型配置字 ==MODENA_L(9)||MM21(16)(枚举 javap 实证Driving$VehicleNameConfig)- M1 页移除点:
CustomSteeringModeFragment.getPreferenceKeyResIdsToRemove(), :224-227 —— 非 Lux/Mann 时把pk_settings_driving_custom_energy_recovery_mode和..._strength两项一起移除 - 主自定义页
MainCustomSteeringModeFragment.kt的移除列表(:64-83)不含能量回收项, 它的门控在更上游:非 Lux/Mann 时入口项本身不可见(见 §2 入口可见性 :204-206)
GT(mach)为什么只有主页那一个滑块
mach(YU7 GT)不是 MODENA_L/MM21 → isLuxOrMann() = false,于是:
- 主驾自定义模式入口不显示(CustomSteeringModePreferenceController :204-206) → 主自定义页(③)到不了
- M1/M2 页能进(CUSTOM_1/2 时),但能量回收两项被移除(CustomSteeringModeFragment :224-227) → ②上没有滑块
- 只剩驾驶偏好主页(①)的预设 Tab + 自定义滑块
N801PROB-132401 的复现车就是 mach,因此该单的全部现象都发生在主页滑块上。 ⚠️ mach 运行时的车型配置字取值未在
VehicleNameConfig枚举中见到专属常量 (javap 0.11.58 全量:MS11=1 … MM21=16、KUNLUN_00=17),「mach 非 9/16」由产品车型 定义背书;如需铁证可在台架dumpsys或属性日志实读该配置字。
4. propId 与值映射:UI 显示几就下发几
App 侧常量(base/settingsBaseLib/.../vehicle/MiCarPropertyIds.java):
| :993 | BRAKING_MODE = Energy.REGENERATION_MODE | 0x61408120 |
|---|---|---|
| :995 | ENERGY_REGENERATION_LEVEL = Driving.ENERGY_REGENERATION_LEVEL | 0x6140757d |
| :997 | ENERGY_REGENERATION_BAN_REASON = Driving.ENERGY_REGENERATION_BAN_REASON | 0x6140757b |
值映射恒等(BaseVehicleProgressPrefController.java):
propValToProgress():142-151 =propVal + offset;progressToPropVal():153-158 =progress - offsetoffset来自@PropertyRange注解,offset() default 0(base/settingsVehicleLib/…/annotation/PropertyRange.java:36),三个 controller 的注解都只写了 lower/upper/defaultVal,没有设 offset- 结论:UI 显示几,下发的就是几,不存在
100-x之类的变换(N801PROB-132401 分析中 已用 bugreport 逐值对账实证,JarviX 的「100-x 反转」猜想被证伪)
调节范围注解:
| Controller | 注解 | 范围 |
|---|---|---|
| BrakingStrengthPreferenceController :46-57 | lower=0/upper=100/default=100,混动经动态代理改 lower=1(:86-104,:54 BRAKING_STRENGTH_MIN_VALUE_HYBRID = 1) | 0 |
| CustomEnergyRecoveryStrengthController.kt :32-36、:46-48 | lower=0/upper=100,混动代理 :178-192 | 0 |
| MainCustomEnergyRecoveryStrengthController.kt :25-28、:41-42 | lower=1/upper=100 | 1~100(无混动代理) |
⚠️ 主自定义页的下限是 1 不是 0——与另两页不同,分析「0 调不到」类反馈时先确认页面。
5. 界面元素与联动
- 滑块本体:
MiCarIconSeekBarPreference(base/settingsBaseUi/…/preferences/ MiCarIconSeekBarPreference.java:42),布局R.layout.micar_ui_pref_icon_seek_bar(:70-72) - 数值标签(thumb 上的数字):
updateThumbText(int progress):126-128 →setThumbText();取整场景的标签时序缺陷与修复见 02-手势语义与取整机制 - 左右端文案:
SeekBarData.mLeftText/mRightText,由BaseVehicleProgressPrefController.setPreferenceTextBelow():113-119 写入 (左=lower、右=upper,单位getUnit()默认为空串) - 雷达图联动:驾驶偏好页右半屏是六维雷达(动力/灵活/经济/舒适/通过/稳定),
DrivingSettingsFragment.java:70/CustomSteeringModeFragment.java:186-191都返回DrivingRadarRightFragment(:47;六轴下标常量 :51-61)。其中「经济」轴BaseRadarDataImpl.updateEconomyValue():76-98 同时消费BRAKING_MODE与ENERGY_REGENERATION_LEVEL(默认实现RadarPropertyInterface.java:42-49)——拖滑块不仅改车,也会实时改变右侧雷达图的经济轴长度
6. 一句话骨架
flowchart TB TAB["预设档 Tab<br/>miauto_driving_settings_fragment.xml:104-108"] -->|写 REGENERATION_MODE=ADVANCED| VM["DrivingFragmentViewModel<br/>brakingStrength observer"] VM -->|==3 可见| SLIDER["MiCarIconSeekBarPreference<br/>:111-113"] SLIDER -->|拖动/点击| CTRL["BrakingStrengthPreferenceController<br/>(或另外两个自定义 controller)"] CTRL -->|"progressToPropVal = progress - 0"| SET["setVehicleProperty 同步下发<br/>BaseVehicleProgressPrefController :233-237"] SET --> PROP[("ENERGY_REGENERATION_LEVEL<br/>0x6140757d")] PROP -.回灌.-> RADAR["雷达图经济轴<br/>BaseRadarDataImpl :76-98"]
手势层面(拖动跟手、点击取整、摸 thumb 豁免)的全部机制在 02-手势语义与取整机制;本篇只管「在哪、谁能看见、值怎么映射」。