05 · preference 封装层与 Settings 使用地图

本篇回答:①组件库的三个滑块 Preference 各是什么;②为什么 MiCarSettings 一个都没用; ③Settings 侧到底谁在用哪个组件。

1. 组件库三个 Preference 封装

类绑定 widget特征
SeekBarPreference(PREF/SeekBarPreference.java,AOSP 移植)框架 android.widget.SeekBar(:64,布局裸 <SeekBar>)完整持久化 + callChangeListener 闸门 + DPAD(:108-133);mUpdatesContinuously 控制拖动中是否落盘(:81)
SliderPreference(PREF/ext/preferenceItem/SliderPreference.java)IconSlider(:28,布局 micar_appcompat_preference_widget_slider_material.xml)有缺口,见 §2
SeekBarPreferenceCompat(PREF/ext/preferenceItem/SeekBarPreferenceCompat.java)IconSeekBar(:76-77 强转)保留 SeekBarPreference 全套逻辑,仅换 widget

SeekBarPreference 的值流(基类语义)

onSetInitialValue: getPersistedInt 恢复(:204-209)
用户拖动: fromUser && (mUpdatesContinuously || !mTrackingTouch) 才连续持久化(:81)
         拖动中只刷 label(:85),onStopTrackingTouch 值变了才落盘(:95-100)
setValueInternal: clamp [mMin,mMax](:353-359) → persistInt(:364)
syncValueInternal: callChangeListener 闸门(:397),被拒则回滚 widget(:400)
min/max 映射: SeekBar max = mMax−mMin(:187),progress = value−min(:198)

2. ⚠️ 组件库版 SliderPreference 的半移植缺口

PREF/ext/preferenceItem/SliderPreference.java:

  • onSetInitialValue 读 getPersistedInt 恢复(:160-165)——会恢复;
  • setValueInternal(:238-255)从不调用 persistInt——不落盘;
  • 也不过 callChangeListener 闸门,监听器(:44-52)直接 setValueInternal + 回调自有 OnProgressChangedListener.onProgressChanged(float)(:333-339);
  • thumb 文本用 (int) sliderValue 截断(:249/:155,非四舍五入)。

结论:只恢复不落盘,疑似半移植 bug(未确认是否有意)。2023-11-18 随 “Preference new UI”(3d9a1023)新建后未再改。

3. SeekBarPreferenceCompat 的口径不一致坑

syncValueInternal(:141-151)回调的是裸 seekBar.getProgress()(不含 mMin 偏移,:144), 而 updateLabelValue 收到的是含 min 的值——回调口径与显示口径不一致。min≠0 的调用方 会拿到小一个 min 的回调值。

4. 为什么 Settings 不用组件库封装

MiCarSettings 全仓 grep micarx.preference.* 零命中。 Settings 用自建体系:

flowchart TD
    SP["settingsBaseUi<br/>common.SliderPreference(1109 行)<br/>extends RichTitlePreference :73"]
    subgraph Widgets["widget 包装(均 implements IFusionSlider)"]
        W1["MiCarSlider<br/>extends material Slider"]
        W2["MiCarIconSlider<br/>extends micarx IconSlider"]
        W3["MiCarIconSeekBar<br/>extends micarx IconSeekBar"]
    end
    SP -->|R.id.carui_slider :618| Widgets
    Sub["miauto.preferences.MiCarIconSeekBarPreference<br/>extends common.SliderPreference :42"]
    Sub --> SP
  • Settings 版多出:可见即可说(ViewDescriptionCompat)、埋点(TrackEvent)、action button、 防抖、取整机制(mProgressChangeTimes/alignAdjustProgress,能量回收 KB 02 篇)。
  • 两版分叉史:组件库版 2023-11-18 出现于 micarx(3d9a1023)后未动;Settings 版 2024-02-27 随”车控代码抽离”(a66a979a6)出现并持续演进。同名两套实现,git 无共同 历史,改哪边都不影响另一边。
  • app/src/main/res/values/themes.xml:77 把 seekBarPreferenceStyle 重指到 Settings 自有 Preference.CarUi.SeekBarPreference.Auto。

5. Settings 侧使用地图(按子模块)

settingsPage 子模块使用点组件
micarChargeSettingsEnergyCurrentController.kt;xml miauto_energy_manager_fragmentcommon.SliderPreference+MiCarIconSeekBar;XML 用 MiCarIconSeekBarPreference
micarDisplaySettingsRearTvScreenAngleIconSliderController.kt;xml miauto_display_atmosphere_settings_fragmentMiCarIconSeekBar;MiCarIconSeekBarPreference
micarDrivingSettingsAccelerateParam/BrakingStrength/VehicleAudioMotionWaveVolume/BaseCustomVehicleProgress/CustomEnergyRecoveryStrength;布局 micar_ui_pref_traction_ratio_seek_barcommon.SliderPreference/MiCarIconSeekBar;布局里 MiCarIconSlider
micarLightSettingsKeyBgLightLevelPrefController、AtmosphereLightBrightnessProgressPrefControllercommon.SliderPreference
micarLockSettingsHeadGateHeight/TailGateHeightPrefController;RoofSunShadeSliderPreference;布局 lock_seekbar_with_two_btnsMiCarIconSeekBar;RoofSunShadeSliderPreference extends common.SliderPreference;布局里 MiCarSlider
micarSafetyServiceSettingsownermode/views/SpeedLimitSliderView.kt(限速滑块,N801PROB-144519)MiCarIconSlider(直封,不走 SliderPreference 族)
micarVolumeReduceEnvironmentalNoiseSliderController、BaseVolume/InCarIntercomVolumeMiCarIconSeekBar/common.SliderPreference
settingsAutoPilotMiCarAutoPilotFixSpd/OverSpdOffsetPrefController;MiCarHeightLimitSeekBarPreferencecommon.SliderPreference+MiCarIconSeekBar;布局里 MiCarIconSlider
micarMediaVideoPlayerActivity.kt + exo_player_control_view.xml:91直接用 material Slider(播放进度)

教训(N801PROB-144519):排查取整滑块要 grep 机制名(mProgressChangeTimes/ alignAdjustProgress)不能只 grep 配置名——SpeedLimitSliderView 是”直封 MiCarIconSlider” 的平行实现,不走 SliderPreference 族,修能量回收时漏网了它。

6. catalog 官方示例(用法组合参考)

catalog/.../seekbar/SeekBarActivity.java + activity_seek_bar.xml 四类同屏:

  • micarx AppCompatSeekBar(含 ignoreClick=true 变体,xml :41/:58);
  • IconSeekBar:按压动画开关、hint CENTER/BELOW、拖动中 setThumbText(:26-37);
  • material Slider:OnChangeListener 回显、touch listener、setIgnoreClick/setPressEnlarge/ setCornersEnable(:68-122),Widget.MiCar.Slider.SliderEnlargeStyle(xml :181-183);
  • IconSlider:按压动画、hint 位置、enable、ignoreClick(:58-104)。

VerticalSliderActivity.java:setValues(60f)、enable/ignoreClick、%.3f 回显、 setStepSize(10) 离散演示(:22-44)。