05 · 排障实战:N801PROB-132401 自定义 slider 数值与中控显示不对应

一单打穿「轻触取整静默改写 + 数值标签滞留」双半面缺陷的全过程复盘。 所有日志论断附 bugreport 真实行号,代码行号以 fuben23 仓 dev HEAD(3f3f7659a,含修复)为准; 修复前(rls-xcd-u-mp-26-v7.0)行号在文中单独标注。 §9 三轮修复史章节行号基准:rls HEAD e8b5a7ba5(V3/PS4 最终内容),修复前基底 f63fe1cdc。

0. 案例档案

项值
工单N801PROB-132401【Lemans-GT】gt主题下能量回收自定义slider调节的数值与中控显示不对应
设备Xiaomi YU7 GT(mach)/ Android 14(API 34)/ 26.07.15.Stable.U.CN(user)
车控平台N801,MCU N801_260715M26RC09GT26RC09_PR
复现率Always(必现)
素材bugreport-mach-UKQ1.240411.001-2026-07-23-10-57-54.txt + 视频(小米办公Pro20260724-144804.mp4,15 张抽帧)
定案置信度high(对抗 review 后 Δ=0)
修复同一 Change-Id I4a34b69c3943fb0574e6fca89b181850db0e766e,Gerrit dev=387202 / rls-xcd-u-mp-26-v7.0=387204;现均为 PS4(V3,GT 门控),本地 commit dev=470f9eaaa / rls=e8b5a7ba5(基底 f63fe1cdc),查询时点 2026-10-13 仍 NEW 未合入。三轮回放见 §9
分析产物车机JIRA/jira-data/N801PROB-132401/analysis/{root-cause.md, summary.json, code-refs.json, review.json};二/三轮素材在 attachments/{提测后测试反馈,第三轮提测后测试反馈}/

「是否误报=误报」字段经对抗裁决为 dispute(误填):与「故障原因=代码bug」+标题「修复中」+标签「2610W02修复」互斥,系模板污染,不作依据。

1. 现象

GT 主题下:设置→驾驶偏好→「能量回收强度」选「自定义」,拖动 slider 到 67 松手,slider 数值标签显示 67;稍后轻触/再触碰后,车辆实际生效值已是 70,而 slider 仍显示 67 长达 6 秒——「slider 调节数值与中控显示不对应」。

视频帧锚点(手机拍摄叠加钟 ≈ 车机钟 −0.5s):

帧手机钟slider 显示同时刻系统值
f_09.png10:58:216770(10:58:19.538 已 SET)
f_14.png10:58:2670(再触摸后跳变)70
f_01.png~10:58:1347(滞留 14s)50(10:57:58.849 已 SET)

2. 根因定案(对抗验证 high):双半面缺陷

「轻触取整静默改写 + 数值标签滞留」——同一控件上「拖动」与「轻触」双语义不一致。

半面 A:摸到 thumb 未拖(0 位移)被误判为点击 → 取整 → 静默改写下发

SliderPreference 用 mProgressChangeTimes <= MAX_PROGRESS_CHANGE_TIMES(=1) 区分「点击 vs 拖动」。 FusionSlider 在 ACTION_DOWN 瞬间会回调一次同值 onValueChange,旧代码无差别 mProgressChangeTimes++, 于是「手指摸到 thumb 但没拖」也累计 1 次 → 命中 <=1 → alignAdjustProgress() 把当前值按 余数≥5 进位吸附到 10 的倍数并直接 SET 下发:

  • SliderPreference.java:81 — MAX_PROGRESS_CHANGE_TIMES = 1(触发门)
  • SliderPreference.java:332 — alignAdjustProgress() 定义;取整公式 originProgress / 10 * 10 + (originProgress % 10 >= 5 ? 10 : 0) (修复前 rls 对应 :325,条件为 mProgressChangeTimes <= MAX;旧 onValueChange 无同值过滤)
  • 运行时实例逐值吻合:47→50、33→30、37→40、66→70、56→60、67→70(6+ 组,见 §3)

半面 B:取整回调时序错误 → 数值标签滞留旧值 6~14s

alignAdjustProgress 在 slider.setValue(70) 之前手动回调 onSliderProgressChanged; MiCarIconSeekBarPreference.updateThumbText() 无参版读 mSlider 当前值,此时组件内部值还是旧的 67 → 数值标签画 67。随后 setValue(70) 只改内部值,thumb/标签不重绘;加上 App 的 * Ignore propId feedback 防回弹机制把属性回显自愈路径也屏蔽了 → slider 显示 67 与系统值 70 持续分裂,直到下一次触摸才从内部值追齐。

  • MiCarIconSeekBarPreference.java:119 — onSliderProgressChanged(修复后 :127 改为 updateThumbText(progress); 修复前 rls :113,旧写法 :115 updateThumbText() 无参读 mSlider 旧值)
  • SliderPreference.java:346 — slider.setValue(originProgress)(内部值改写点;修复前 rls :336)

3. 证据链:bugreport 三视角逐值对账(全部真实行号)

主 txt:bugreport-mach-UKQ1.240411.001-2026-07-23-10-57-54.txt,propId=0x6140757d。

3.1 主案:拖到 67 → 轻触 → 静默变 70 → 滞留 6s → 再触摸追齐

行号时刻内容解读
79274510:58:16.703onStartTrackingTouch() mSliderValue = 50拖动开始
792766~79320116.725~16.937>>> Done set propVal: 53..67 逐值 12 次SLIDER_NORMAL 拖动跟手下发,正常
79320210:58:16.938onStopTrackingTouch() mSliderValue = 67松手停 67,不取整(>1 次变化)
794854/79485510:58:19.536onStart/onStopTrackingTouch mSliderValue = 67(同毫秒)真·轻触,0 位移
79485610:58:19.536onSliderStopTrackingTouch() mOriginProgress:0.0, slider.getValue():70.000000alignAdjustProgress 已把内部值吸附 67→70
79486210:58:19.538>>> Done set propVal: 70 to propId: 0x6140757d静默改写下发
79490010:58:19.627CarPropertyCacheManager: BrakingStrengthPreferenceController@160834759|DrivingRadarRightFragment| onChangeEvent id=0x6140757d v=70回显 70(holder 标签同时是页面身份铁证)
79490110:58:19.627* Ignore propId: 0x6140757d feedback: 70防回弹拦截 → slider 失去自愈机会
79490310:58:19.627BatteryHandler: onPropertyChange ... mValue=70下游(MIoT 上报链)拿到 70
79760710:58:25.528onStartTrackingTouch() mSliderValue = 70再触摸时内部值已是 70,视觉追齐(f_14)

视觉滞留实证:19.5s 系统值已 70,视频 f_09(21s)/f_10/f_11/f_13 slider 仍显示 67,25.5s 再触摸才跳 70——分裂 6s。

3.2 前段同款:47 滞留 14s

行号时刻内容
232478/23247910:57:58.847onStart/onStopTrackingTouch mSliderValue = 47(同毫秒轻触)
23248010:57:58.847onSliderStopTrackingTouch() slider.getValue():50.000000
23248610:57:58.849>>> Done set propVal: 50(47→50 静默改写)

f_01(≈车机 10:58:13)slider 仍显示 47,10:58:15.192 一次无 SET 的触摸把视觉追齐到 50(f_04)—— 该手势无 onSliderStopTrackingTouch/SET 日志,与「纯视觉追齐」互证。

3.3 取整公式运行时验证(余数≥5 进位)

行号轻触时刻mSliderValue → getValue()SET
207499~20750810:57:27.02933 → 30.030(余数 3,不进位)
232478~23248610:57:58.84747 → 50.050(余数 7,进位)
212080~21220510:57:33.28066 → 70.070(余数 6,进位)
794854~79486210:58:19.53667 → 70.070(余数 7,进位)

公式 mRoundUpStep/2 + mRoundUpStep%2 = 10/2+0 = 5 → 阈值=余数≥5(标准 half-up)。 注意:分析初稿曾误写「≥6」,对抗 review 已修正为 ≥5;现有实例无 x5 用例,修复验收必须补 45/55/35 边界(见 06 篇)。

3.4 中间件与 MCU:原值透传(MCU 段经纠正后实证)

  • App→3-MW 段铁证:milogcat CARSIGNAL(FS/data/milog/milogcat/CARSIGNAL/milogcat) prop client set vals [N] 与 pub p=0x6140757d,a=0,v=N 逐值相等—— 如 156700 行 set[47]@10:57:35.604 → 156942 行 pub v=47@35.715;主 txt SET:70@794862 → pub 70@794900 同毫秒。
  • MCU 段(纠正后实证):初版 root-cause 引用 send to pps ok, n=7, d=[202,166,165,47,2,0,X] 称「d[3]=回收强度原值透传」——已证伪:d[3] 在全部 175 条中恒为 47,包括 App 正在下发 4/7/13/…/100 的拖动过程(set vals [4]@148746 同毫秒 pps 同样 d[3]=47),是常量字段。 正确锚点=末字节 d[6] = App 下发值 + 1,逐 ms 跟踪 12/12 样本吻合(本文复核): SET 4/7/13/18/23/28/32/37/41/45/50/54(148746149023)→ d[6] 5/8/14/19/24/29/33/38/42/46/51/55, 每条 pps 落后对应 SET 不到 1ms;58 段复证:SET:47@10:57:58.162(169290)→ d[6]=48@58.162728(169293) 同毫秒,SET:50@58.849 → d[6]=51@58.851(169835)。 序号假设已排除:相邻两包 d[6] 61→66(149029@23.040 → 149039@23.058)差 5, 恰等于期间 SET 60→65 的差值,非逐包自增。 边界(诚实):CARSIGNAL 主文件覆盖 10:51:2210:58:01.77,不含 10:58:19 的 67→70 改写段 (轮转 gz 均为更早时段)——「MCU 收到 70」无直接实证,属由 12/12 跟踪关系做出的合理外推; 该段下游值由主 txt BatteryHandler(794903 mValue=70)旁证。 另:全链路未观察到任何字节呈 100-N 规律,100-x 反转无迹象。 (注:本纠正实证产自 KB 核对阶段;JIRA 已发评论不含 d[3] 论断,无需更正。)
  • 发布限速瞬态(非根因):拖动 213ms 写 12 值,CarPropertyCacheManager 回显仅 5 值(40~90ms 间隔), 终值 ≤150ms 收敛——中间件仅瞬态滞后,稳态一致。

4. 对抗裁决:已被证伪的论断(别再提)

论断(出处)证伪证据
JarviX:写属性做了 100-x 反转(51→49)BaseVehicleProgressPrefController.java:142/153 propValToProgress/progressToPropVal 恒等映射(offset=0);CARSIGNAL set[47]→pub v=47 原值;主 txt SET/pub/BatteryHandler 逐值相等。全链路无任何反转
JarviX:取整在 MiCarX 组件库 CompSlider.kt:55 roundToInt运行时日志签名全部是 Settings 仓 SliderPreference 输出(onSliderStopTrackingTouch/alignAdjustProgress 路径/>>> Done set propVal);松手无 ±1 跳变(f_05/f_09 均 67 稳定),roundToInt 未生效。指错了文件与层
H2:GT 主题走另一条 controller 路径分叉页面身份铁证:holder 标签 BrakingStrengthPreferenceController@160834759|DrivingRadarRightFragment(794900)+ pageId=drive_preference;mach 上 M1 二级页能量回收项被 CustomSteeringModeFragment.java:225-227(isCustomEnergyRecoveryEnabled()=isLuxOrMann()=false,MiCarSettings.java:1227)移除,GT 唯一可拖的就是主页 slider。三处 controller 取整配置完全相同(Braking:112 / Custom:58 / MainCustom:60 全部 setValueRoundUp(true)+SLIDER_NORMAL)
H4:M1/M2 预设属性分流全程操作 0x6140757d;M1/M2 属性(REGENERATION_PARAMETER_MODE1/2_STATUS)在 mach 页面被移除、视频未经过

「gt主题」的真实含义:页面拓扑 + 测试语境限定词(GT 车上只有主页有这个 slider),不是代码分叉。

5. 「3.2.4 称已修 → 3.2.6 仍在」之谜:修错了层

工单版本链:张硕 07-31 报 micarx_lib 3.2.2.1 有问题 → 组件库开发称 3.2.4 已修 → 韩国良 09-02 实测 3.2.6 仍存在。

谜底:取整改写代码 100% 在 Settings 仓 base/settingsBaseUi/SliderPreference.java,不在 MiCarX 组件库。 组件库从 3.2.2.1 升到 3.2.6 对这半边天然无效——修错了层。 (视觉滞留半边的渲染责任在 IFusionSlider 一族,大概率涉及组件库,但该半边未实证定责; 本次修复在 Settings 侧用 updateThumbText(progress) 参数化绕过,见 §6。)

6. 修复 V1(=PS1/PS2 内容,fermi 实机验证)

⚠️ 本节描述的是该 CL 的第一版功能内容(V1)。V1 提测后出现「连续拖动松手被取整」回归, 经 V2(PS3)、V3(PS4)两轮修订才收敛——完整演进见本文 §9 三轮修复史。 本节 commit cc27d08ba 为 V1 本地提交;最终版(V3)rls 本地 commit 为 e8b5a7ba5。

[Bugfix][All-Vehicle][Driving] 修复能量回收自定义slider取整后数值标签滞留旧值的问题 Gerrit:dev=387202、rls-xcd-u-mp-26-v7.0=387204

三处改动(2 文件):

  1. 视觉跟随 MiCarIconSeekBarPreference.java:127:updateThumbText() → updateThumbText(progress) ——用传入参数,不再读 mSlider 旧值。取整后标签立即跟随(实测点 79→thumbText=80 立即)。
  2. 收窄触发面 SliderPreference.java:337:条件 mProgressChangeTimes <= MAX → == MAX ——0 次变化(摸 thumb 未拖)不再触发取整。
  3. 同值回调过滤 SliderPreference.java:230:onValueChange 计数加 mSliderValue != getIntSliderValue(value) 条件 ——过滤 FusionSlider ACTION_DOWN 瞬间的同值回调,「摸 thumb」不计入点击判定。

commit message 自测四条(fermi xcd_cn_a17 mock 环境,实操记录见 06 篇):

  • 点击轨道 79→取整 80 下发,数值标签立即显示 80 ✅(86420 ByDesign 兼容)
  • 点击 45→取整 50,边界 ≥5 进位正确 ✅
  • 拖到 67 松手后摸 thumb→值保持 67 不再被改写 ✅
  • 拖动 58→68 跟手下发与文本实时刷新正常 ✅

影响面分级

级范围说明
A(行为修复)3 个取整 controller:BrakingStrengthPreferenceController:112 / CustomEnergyRecoveryStrengthController.kt:58 / MainCustomEnergyRecoveryStrengthController.kt:60全是能量回收;全仓 setValueRoundUp(true) 普查仅此 3 处
B(代码路径变、行为等价)16 个 MiCarIconSeekBarPreference 实例updateThumbText(progress) 参数化,非取整场景参数值==mSlider 当前值
C(零影响)纯 SliderPreference ×10、RoofSunShadeSliderPreference(micarLockSettings)未开 setValueRoundUp,不进入取整路径

7. 残留与边角(诚实记录)

  1. 修复后「摸 thumb 顺势拖 1 格」会取整(旧版不会):1 次真实位移 == MAX 仍触发——这是保留的 点击取整语义的自然外延,产品口径未裁决,暂记为已知行为差异。
  2. 取整模式拖动首值晚一个值下发:roundup 模式下首个 onValueChange 被吞(不下发),松手时 onSliderStopTrackingTouch→syncValueInternal 兜底强制终值。实证:208195~208211 tap@30 时 SET:31@10:57:28.096(原始值泄漏一帧)→ SET:30@28.097 兜底。保留未修(跟手语义所需)。
  3. RoofSunShadeSliderPreference 同款旧写法未修:遮阳帘 slider 是 MiCarIconSeekBarPreference 一族, 但因未开 setValueRoundUp,不触发取整路径,本次不带改。
  4. GT 主题 slider 视觉/触摸错位 ~96px(独立待查):视觉 thumb(蓝填充右端)≠ 触摸判定位置, GT 主题下「摸 thumb」极易误点为轨道——本案误触改写的物理放大器。mock 实操中想摸 thumb(x=1376) 实际落到了轨道值 79 即实证。疑似 GtSliderThemeDelegate track inset/绘制偏移,组件库侧待立项。
  5. 「中控显示」的文字指代未在工单写明:行为闭环已由视频完成(slider 47/67 vs 系统 50/70), 若报告人特指仪表屏/手机 App 需再确认——两种读法根因相同。

8. 教训清单( transferable )

  1. slider 类「显示与下发不对应」先查取整/吸附回调时序:setValue 前后的回调里读组件当前值, 拿到的可能是旧值——文本更新一律用回调参数。
  2. 「点击 vs 拖动」判定计数必须过滤同值回调:ACTION_DOWN 的同值 onValueChange 是控件常态, 不过滤就会把「摸一下」误判成「点一下」。
  3. 防回弹(Ignore feedback)是双刃剑:它屏蔽回显自愈路径,任何「静默改写下发」都会被视觉滞留放大。
  4. 修组件库前先确认代码归属仓:运行时日志签名(类名/tag)是最快的归属判据;本案组件库连升 3 版无效,因为代码在 Settings 仓 settingsBaseUi。
  5. 检索必须 .kt+.java 双扩展名:MiCarSettings 大量 controller 是 .kt(如 CustomEnergyRecoveryStrengthController.kt),只 grep .java 会漏。
  6. 对抗 review 的价值:初稿「阈值≥6」「setValue 立即生效」两处不精确,被红队攻击后修正为 「≥5」「内部值改写+视觉滞留」——机理更硬,修复落点也因此从「移除取整」(会违背 86420 ByDesign) 改为「视觉跟随+收窄触发面」。
  7. 车端 pps 负载字节勿凭单一巧合值下论断:初版把 d=[202,166,165,47,…] 的 d[3] 当强度透传, 只因它恰好恒等于当时的设定值 47;全量 175 条统计立刻证伪(常量字段),正确锚点是末字节 d[6]=下发值+1。字节语义论断必须「全样本统计 + 变化序列跟踪」双验证,单点巧合不足为证。

9. 三轮修复史:V1 → V1 回归与 V2 → 第三轮 GT 抖动与 V3(PS1~PS4)

同一 Change-Id I4a34b69c3943fb0574e6fca89b181850db0e766e 的四个 patchset 承载三次功能修订 (PS2 仅改 commit message)。本节行号基准:rls-xcd-u-mp-26-v7.0 HEAD e8b5a7ba5(=PS4 内容); 修复前基底为 f63fe1cdc。Gerrit:dev=387202/rls=387204, 查询时点 2026-10-13 均 NEW 未合入。本地 commit:dev=470f9eaaa / rls=e8b5a7ba5。

9.0 时间线总览

轮次补丁(Gerrit 记录时间)测试反馈一句话定案
原案(2026-07,出厂包,GT)—摸 thumb 未拖,值被静默取整改写,标签滞留 6sV0 双半面缺陷(本文 §1~§6)
V1PS1(10-13 02:10)+PS2(仅 message)(修复提测)标签参数化 + 同值过滤 + ==1 门;但计数仍在下发之后
二轮回归PS3(10-13 07:00)= V2「连续拖动松手也被取整了」V1 计数后置被下发覆盖,==1 门恒真(§9.2)
第三轮PS4(10-13 12:07)= V3「拖动跟手 OK,但同位置连点偶发抖动跳值」(GT)GT 组件库强制 ignoreClick,点击手势不存在,==1 门被按下微抖随机击穿(§9.3)

9.1 V1(PS1/PS2):原案修复

  • 针对现象:摸 thumb 0 位移被误判点击 → 静默取整改写 + 标签滞留旧值(本文 §1~§3)。
  • 修复内容(三件套):① MiCarIconSeekBarPreference.updateThumbText(progress) 参数化 (e8b5a7ba5 :121,V0 为 :115 无参读 mSlider 旧值);② onValueChange 计数加 mSliderValue != getIntSliderValue(value) 同值过滤;③ 取整门 <= 1 → == 1 (V0 在 f63fe1cdc :327)。
  • 证据锚点:本文 §3 全部;fermi mock 自测四条(§6)。
  • 遗留陷阱:V1 把「同值不计数」的比较放在「count>0 下发」之后——埋下了二轮回归。

9.2 V1 回归与 V2(PS3):「连续拖动松手被取整」

  • 反馈现象:二轮提测包(mach)上,正常连续拖动(如 80→34 一路跟手)松手后, 值仍被吸附到整十(34→30)——拖动语义被破坏。
  • 根因机理(计数恒 1 连锁): V1 的 onValueChange 顺序是「先按 count>0 下发,后比较计数」。下发链 onSliderProgressChanged(:364)→ syncValueInternal(:995)→ callChangeListener 成功(:1006)→ setValueInternal(:931)→ mSliderValue 被覆盖为当前值(:933-934)。 于是从第 2 个变值回调起,「下发后才执行」的 mSliderValue != 新值 比较恒为同值 → 计数永远停在 1 → 松手时 == 1 门恒真 → 任何手势(含连续拖动)松手必取整。
  • 证据锚点(二轮 bugreport attachments/提测后测试反馈/bugreport-mach-…-2026-10-13-10-58-53.txt, 全量手势切片统计,本 KB 复核):
    • 58 段手势切片中 34 次松手被取整,其中 30 例拖动期已下发 >1 个不同值仍被取整 (多值拖动本应 count>1 不取整)——回归实锤;
    • 代表锚点:自 :112677 起拖、跟手 20 个不同 SET 值,松手仍 75→80 (onStopTrackingTouch :113662 / slider.getValue():80 :113663); 自 :92959 起拖 8 个 SET 值,松手 16→20(:93215/:93216);
    • 对照:7 月出厂包同法统计 39 切片 / 11 次取整 / 0 例多值拖动取整(全是 ≤1 次变化的轻触);
    • 提测包 dex 字节码 if-eq 顺序实证「计数在下发之后」(上一会话反编译取证)。
  • 台架验证盲区(关键教训):台架 vehicled 不持久写入,callChangeListener 经 onDataVerifyFailed 回滚返回 false,syncValueInternal 走回滚分支 slider.setValue(mSliderValue)(:1009)——不覆盖 mSliderValue → 计数比较正常工作 → 台架上计数恒正常,该回归在台架物理上无法复现。验证此类「计数/状态时序」回归, 必须用正式环境包(或打计数日志)验证,台架绿不等于无回归。
  • V2 修复内容(PS3): ① onValueChange 计数移到下发之前(:229-231 在 :234 下发门之前),且下发门 > 0 → > 1(:234,保持「首个变值不下发」语义不变); ② alignAdjustProgress 先 slider.setValue(:357)再手动回调刷新(:360)—— 消掉 V1 顺序下 syncValueInternal 读到取整前原值导致的「原值泄漏一帧」 (如 45→50 先把 45 发给车辆)。

9.3 第三轮:GT 点击抖动与 V3(PS4)

  • 素材:attachments/第三轮提测后测试反馈/(视频 小米办公Pro20261013-181624.mp4 + bugreport bugreport-mach-…-2026-10-13-18-11-58.txt);测试动作 = 拖动 + 同位置连点 8~15 次。

  • 现象实证(三轮 bugreport 全量统计,本 KB 复核):共 149 段手势切片 = 17 次拖动 + 132 次无跟手下发的触摸(点按/摸 thumb),其中 126 次值零变化(118 次全程零 SET、 8 次仅一次同值兜底 SET),仅 6 次在松手后 ~1ms 偶发吸附 SET,方向随机(向下 4 次/向上 2 次):

    行号(start/stop → SET)时刻跳变
    134976/134977 → 13498018:09:44.462→.46334→30
    138523/138528 → 13853118:09:49.417→.42357→60
    142515/142518 → 14252118:09:58.035→.03856→50
    173020/173021 → 17302418:11:02.083→.08421→20
    176511/176515 → 17651818:11:07.238→.24527→30
    181539/181543 → 18154618:11:15.885→.89237→40

    视频帧另示触摸点(录屏白点)与数值标签位置脱节——GT 96px 视觉/触摸错位,见 08 篇。

  • 根因(双层): ① 组件库 GT 强制 ignoreClick=true(08 篇 §3.4 反编译实证):点轨道 = 就地 grab 不跳值, DOWN 被 computeTouchOffset 平移到当前值,|Δ|<1e-4 不派发——「点轨道跳值」手势在 GT 上根本不存在(126/132 零变化即此体现;无微抖的纯点连 :390 的兜底 SET 都不触发); ② V2 的 ==1 门在 GT 失去「点击 vs 拖动」的区分力,只剩「按下期间手指微抖跨过 int 边界 (Math.round .5 线)」这一条击穿路径——跨了,count 就是 1,松手即吸附跳变,方向取决于 微抖方向,完全随机 = 测试看到的「点击抖动」。

  • 对照组(反向佐证):按键背光亮度在 GT 无抖动。灯光→按键背光亮度 (KeyBgLightLevelPrefController.java,XML miauto_lights_settings_fragment.xml:266) 与能量回收滑块是同一控件类 MiCarIconSeekBarPreference、同一 controller 基类、同一台 mach、同一 GT 主题、同一组件库版本——第三轮实测它无任何「抖动」反馈。唯一变量: 它不开 setValueRoundUp(取整分支不进,微抖跨界也不吸附)→ 无抖动。坐实第三轮根因 = 「GT 库强制 ignoreClick × 取整门」的组合产物,而非控件/组件库本体坏;PS4「GT 关取整」 本质就是把能量回收调成与对照组一致。附注:① 它 XML settings:ignoreClick="true" (:270)又被代码 setIgnoreClick(false)(:48)覆盖——这是 Settings 层字段 (SliderPreference.mIgnoreClick,:493/:497),与组件库 GT 强制 ignoreClick 是两层, 勿混;② 它的松手吸附整数来自 setStepSize(1f)(:50)有级调节链(syncValueInternal 的 mStepValue 取整,:997-1002),与整十吸附是两套独立机制;③ GT 主题下它对点击同样不跳值 (组件库强制),产品裁决「GT 只支持拖动」对它天然成立。

  • 历史线:7 月出厂包(GT,<=1 旧门 + 无同值过滤)任何触摸松手必取整(132401 原案) → V1 杀同值计数(引入回归)→ V2 修回归(计数前置)→ V3 GT 关取整。

  • V3 修复内容(PS4,2 文件 + 3 处 override): 取整门加 !slider.isIgnoreClick()(SliderPreference.java:345-346); IFusionSlider.kt:54-56 新增 isIgnoreClick() 接口默认 false; MiCarIconSlider.kt:64-67 与 MiCarSlider.kt:40-43 override 委托组件库 BaseSlider.isIgnoreClick() 返回真实值。

  • 修复语义:V3 后 GT 连点/摸 thumb 永不跳值(拖动跟手不受影响);Normal 主题 (isIgnoreClick=false)86420 点击跳整十完整保留;组件库若在未来版本恢复 GT 点击跳值 (3.2.7,截至 10-13 release/snapshot 仓均未发布),isIgnoreClick 门自动恢复取整—— 前向兼容设计,无需再改 Settings。

  • micarx 反馈:「除最后数值(标签文本,Settings 资产)外其余为组件库逻辑」+「升级到 3.2.7」。

  • ⚠️ 诚实边界:V3 的 GT 路径未经实机验证(V1/V2 自测在 fermi Normal 完成)。验收用例 = mach 连点 10 次值不跳变 + fermi Normal 主题点击取整保留(79→80、45→50),见 06 篇 §7 T8/T9(mach 台架 mock 通道见 06 篇 §9)。

  • 遗留:① 往返净零位移(53→54→53)误判点击(理论级,未处理;更优解 = 与上次回调值比较的 mLastCallbackValue 方案,被产品裁决否决,见 §9.4); ② RoofSunShadeSliderPreference.kt:152-155 同款「回调里读当前值」旧写法未修 (该页未开 setValueRoundUp,不触发取整路径,观察); ③ 7 月 ROM 组件库构建版本与「点按即变值」行为矛盾(08 篇 §6.4/§8,待复测)。

9.4 产品裁决与方案选型(2026-10-13,权威需求基准)

产品最终裁决(需求口径):

  • GT 主题:只支持拖动;点击不生效(仅按压放大缩小动画,无值变化)→ 取整彻底关闭;
  • 普通主题(GT 关闭后的深浅色):拖动随手变化 + 点击定位到手位置 + 数值只能固定整十;
  • 关键边界:普通主题下「贴 thumb ±1 点击」也必须取整(点击数值只能是整十)。

方案选型:否决「净位移」判别方案(如 53→54→53 净位移 0 判点击)——它无法覆盖 「贴 thumb ±1 点击必须取整」的需求(±1 会被净位移判成拖动而不取整,与需求直接冲突); 选定 isIgnoreClick 主题门控(取整门 = mIsRoundUpWhenClick && !slider.isIgnoreClick() && mProgressChangeTimes == 1)——即 PS4 已推内容,代码无需再改。 组件库侧立项(GT 按压白点错位 + 是否放开 ignoreClick)将引用 08 篇(保持「待立项」)。

10. 关联

  • 历史前科:N801PROB-86420「非整十数点击按四舍五入跳到整十」关单 By Design——本案是它的 「可见性半边失效」新缺陷半面,不能套用 86420 关单,也不能简单移除取整。
  • mock 配方与验收矩阵:06-台架mock配方与测试矩阵.md
  • 速查:07-速查卡.md

11. 第四轮:平行实现漏网——车主管理模式限速滑块(2026-10-14)

现象与定案

测试反馈:「车主管理模式」弹窗(行车安全域)里最高车速限制滑块没修到。 录屏帧序列 114→110→130→114→110:非拖动突变(114→110)=轻触 thumb 被静默吸附, 与原案同一形态。

根因:它根本不是 SliderPreference 族。micarSafetyServiceSettings/.../ownermode/views/SpeedLimitSliderView.kt 直接二次封装 MiCarIconSlider,自带一份手抄的「点击吸附整十」逻辑(:169-171 计数 + :196-210 alignAdjustProgress),带着修复前的全套旧缺陷:

  1. 计数无差别(每个 fromUser float 回调都 mProgressChangeTimes++,同值/微抖也计);
  2. 门是旧 > MAX 语义(= ≤1 取整)——零位移摸 thumb(count=0)松手也吸附改写;
  3. 无 GT ignoreClick 门——GT 上退化为随机吸附。 (它没有标签滞留问题:updateSliderShowValue 先写值再写字。)

修复(新 CL,与主修复同构)

  • dev=387772 / rls=387775 (Change-Id I6db288ddc4e91509e5fcfff56e405e8aca995d87,Jira 挂独立工单 N801PROB-144519「点击slider左右侧已设值会改变」,该文件双分支 md5 一致,一处改两边生效;分析评论已发于该单 2026-10-14)
  • 改动:①计数挪进 displayInt != mCurrentSpeedInternal 分支内(只数真实变值); ②门改 == MAX_PROGRESS_CHANGE_TIMES(恰好 1 次真实位移才取整); ③加 mSlider.isIgnoreClick() GT 门(复用 PS4 在 MiCarIconSlider 加的 override)

教训(排查法)

找取整滑块不能只 grep 配置名(setValueRoundUp)——平行实现不会调用它。 正确姿势:grep 机制名 mProgressChangeTimes / alignAdjustProgress / RoundUp。 全仓排查结果:除 SliderPreference 族(已修)与 SpeedLimitSliderView(本轮修)外, 还有 base/settingsBaseUi/.../SeekBarPreference.java:151 一份更老的 ≤1 门休眠代码 (SeekBar 变体,当前无使用方,建议顺手清理或标记,勿复活)。