01 · 功能与页面结构(新手入门)

读完本篇你应该能:讲清「能量回收强度」是什么;在代码里找到三处滑块的挂载点; 说清它什么时候可见、什么时候置灰、在哪些车型上存在。

代码基线:fuben23_micarsettings/MiCarSettings @ rls-xcd-u-mp-26-v7.0 (HEAD e8b5a7ba5 = 修复 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_softSOFT = 1「回收强度为 1%」(:107)
适中..._braking_strength_moderateMODERATE = 4「回收强度为 35%」(:108)
标准..._braking_strength_standardSTANDARD = 2「回收强度为 100%」(:109)
自定义..._braking_strength_advancedADVANCED = 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 不同:

#页面FragmentXML(rls 行号)Controller
①驾驶偏好主页DrivingSettingsFragment(:64-65 绑 R.xml.miauto_driving_settings_fragment)miauto_driving_settings_fragment.xml:111-113,key=pk_settings_driving_braking_strengthBrakingStrengthPreferenceController.java
②M1/M2 自定义页(Lux/Mann)CustomSteeringModeFragmentmicar_custom_steering_mode_fragment.xml:35-37,key=pk_settings_driving_custom_energy_recovery_strengthCustomEnergyRecoveryStrengthController.kt
③主驾自定义页MainCustomSteeringModeFragmentmicar_main_custom_steering_mode_fragment.xml:13-15,key=pk_settings_driving_main_custom_energy_recovery_strengthMainCustomEnergyRecoveryStrengthController.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-202 setVisible(getIntProperty(BRAKING_MODE) == Energy.RegenerationMode.ADVANCED); 另有开页缓存初值 :137-142 + DrivingFragmentViewModel.registerBrakingStrengthObserver 实时跟随 :144-151
  • M1 页:CustomEnergyRecoveryStrengthController.kt 缓存初值 :61-66、observer :72-76、 onPropertyManagerPrepared :154-157
  • 主自定义页:MainCustomEnergyRecoveryStrengthController.kt observer :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,于是:

  1. 主驾自定义模式入口不显示(CustomSteeringModePreferenceController :204-206) → 主自定义页(③)到不了
  2. M1/M2 页能进(CUSTOM_1/2 时),但能量回收两项被移除(CustomSteeringModeFragment :224-227) → ②上没有滑块
  3. 只剩驾驶偏好主页(①)的预设 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):

:993BRAKING_MODE = Energy.REGENERATION_MODE0x61408120
:995ENERGY_REGENERATION_LEVEL = Driving.ENERGY_REGENERATION_LEVEL0x6140757d
:997ENERGY_REGENERATION_BAN_REASON = Driving.ENERGY_REGENERATION_BAN_REASON0x6140757b

值映射恒等(BaseVehicleProgressPrefController.java):

  • propValToProgress() :142-151 = propVal + offset;progressToPropVal() :153-158 = progress - offset
  • offset 来自 @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-57lower=0/upper=100/default=100,混动经动态代理改 lower=1(:86-104,:54 BRAKING_STRENGTH_MIN_VALUE_HYBRID = 1)0100(混动 1100)
CustomEnergyRecoveryStrengthController.kt :32-36、:46-48lower=0/upper=100,混动代理 :178-1920100(混动 1100)
MainCustomEnergyRecoveryStrengthController.kt :25-28、:41-42lower=1/upper=1001~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-手势语义与取整机制;本篇只管「在哪、谁能看见、值怎么映射」。