04 · IconSeekBar 与 IconSlider 精读
两个组件是 androidbasewidget 模块的车机定制壳:在基础滑杆上加 thumb 文字/图标、 textStart/textEnd 两端文案、hint 位置、Folme 弹簧按压动画。二者代码高度对称但 不同源(01 篇)。
1. IconSeekBar(int progress 生态,1131 行)
构造要点(ABW/IconSeekBar.java:115-210)
- 三个构造强制包
ContextThemeWrapper(Widget_MiCar_IconSeekBar_DayNight)(:115-121), XML 传 theme 也会被覆盖。 - GT/Ultra 下 delegate 改写:
mTextStart/mTextEnd被强制替换为"0"/"100"(:176-179,常量 MIN/MAX_VALUE_GT 于 :40-41)。 - 构造末尾
clearThumbDrawable()(:208)——原生 thumb 不画,只画自绘 bg+icon+text。 checkValue():max==min抛 IAE(:212-216;setMax/setMin 触发 :256-265)。 IconSeekBarTest 的边界用例钉的就是它。
绘制管线
flowchart TD OD["onDraw :347-370"] --> S1["canvas 按 mScale Y 缩放 :360"] S1 --> S2{"GT/Ultra?"} S2 -->|是| S3["delegate.setupOnDrawClipPath<br/>轨道裁成 thumb 两侧两段 :361-367<br/>GtSeekBarThemeDelegate:82-106"] S2 -->|否| S4["整轨(Normal)"] S3 --> S5["super.onDraw(框架画轨道)"] S4 --> S5 S5 --> S6["drawIconAndText :377-554"] S6 --> S7["thumb 中心 X = availableWidth×(p−min)/(max−min)<br/>clamp [thumbPadding, W−thumbPadding] :381-383"] S7 --> S8{"内容形态"} S8 -->|仅文字| S9[":488-494"] S8 -->|仅图标| S10[":498-504"] S8 -->|图标+文字| S11["按 mIconGravity 四方位排布 :507-553"]
- hint 文案:
hintTextBelow时画滑块下方(:396-405);否则 GT/Ultra 由 delegate 画在轨道 上方且按压时按 thumb 左右边界 clip(GtSeekBarThemeDelegate:120-149)。 clipPathIfNeeded()返回 false(:372-375),关掉父类 micarx AppCompatSeekBar 的整轨 clip (AppCompatSeekBar:129-148),改由自己的 delegate clip 接管。- 高度:GT/Ultra 经
adjustMeasuredHeight加 hint 高度(:226-232)。
按压动画(两组,弹簧插值器枚举 SpringInterpolatorType :1106-1130)
| 组 | 触发(delegate 分流) | 内容 | 回弹 |
|---|---|---|---|
Normal 组 startBgPressAnimation(:652-772) | shouldStartPressAnim()=false → 走 PressAnimHelper 路径,带 thumb 命中检测(:637-646) | 文字上移、thumb 高×1.5、宽×0.65、圆角×1.1、轨道 scaleY×1.05 | startUpBgPressAnimation :792-912 |
GT/Ultra 组 startBgExpandAnimation(:915-1007) | delegate 直接受理 | 左右扩张 offset + 文字上移/放大 + 轨道增高(mTrackOffsetYScale→mTrackExpandHeightScale/1000,千分位整数资源) | startUpBgExpandAnimation :1010-1100 |
2. IconSlider(float value 生态,1144 行)
构造与动画抑制
- delegate 创建于 :134;GT/Ultra 强制 thumb 背景换 GT 资源、icon 置 null、textStart/End 改写(GtIconSliderThemeDelegate:23-46)。
clearCustomThumbDrawable()(:191)清掉 BaseSlider 的 thumb。startCustomAnim()/reverseCustomAnim()override 恒 true(:340-347)——一举抑制掉 BaseSlider 的 mTrackingAnim 和 material Slider 的 mSliderAnim(03 篇 §5),IconSlider 完全用自研动画。
几何与绘制钩子
onLayout里setThumbPadding(mThumbWidth/2)(:289)——把 BaseSlider 触摸映射内缩半个 thumb 宽,经getEffectiveThumbPadding进入 generateTouchPosition 公式(02 篇 §2)。- 轨道 path hook:
setTrackPath→delegateapplyTrackPath(:350-356,GT 自绘圆角矩形, 中心=(h+hintH)/2); - 激活/非激活轨道:
drawActiveTrack/childDrawInactiveTrack交 delegate 自绘 (:359-381,GT 画线 GtIconSliderThemeDelegate:65-106,clamp 越界); - 轨道中心:
calculateTrackCenter→getSliderCenterY(:328-337,GT/Ultra 下移 (height+hintTextHeight)/2)——GT 主题”轨道中心不在 view 中心”由此而来。
按压偏移的边缘特判(GT/Ultra)
applyPressOffsets(GtIconSliderThemeDelegate:158-167):
value ≈ MAX_VALUE_GT(100) → 只向左扩 [edge, 0]
value ≈ MIN_VALUE_GT(0) → 只向右扩 [0, edge]
中间 → 双侧扩 [expand, expand]
⚠️ 判定用常量 MAX_VALUE_GT=100/MIN_VALUE_GT=0(IconSlider.java:39-41)而非真实
valueTo/valueFrom,比较阈值 DISTANCE_THRESHOLD=1E-5。量程不是 0~100 的 slider,边缘
特判永远失效,恒走中间档——接新量程时注意。
与 IconSeekBar 的差异对照
| 维度 | IconSeekBar | IconSlider |
|---|---|---|
| 值模型 | int progress(ProgressBar) | float value(BaseSlider) |
| 回调 | 框架 OnSeekBarChangeListener | Slider.OnChangeListener/OnSliderTouchListener |
| 点击抑制 | delegate shouldTransformTouchEvent → AppCompatSeekBar transform | delegate shouldIgnoreClick → setIgnoreClick(true)(:570-572) |
| 轨道增厚量 | mTrackOffsetYScale(比例,继承 micarx AppCompatSeekBar :48) | trackOffsetY(像素,继承 BaseSlider :256) |
| tooltip label | 无 | 继承 BaseSlider TooltipDrawable(默认样式 gone) |
| 按压偏移判定 | int progress 等值比较 | float + 1E-5 阈值 |
3. VerticalSlider 补遗(垂直自绘版,809 行)
- extends View 独立实现,拷贝 BaseSlider 骨架(values/stepSize/snapTouchPosition/ transformTouchEvent 同名机制)。
- 轨道 clip 中挖掉所有 thumb 区域:
setClipForAllThumbs(:234-246,Path.Op.DIFFERENCE); inactive 两段 + active 一段 drawLine(:274-287)。 - thumb 按压横向拉宽动画:Folme
useValue+ ValueProperty,width→2×width(:397-467), 仅 GT/Ultra 且drawablePersistence=false。 - 宽度由 track 宽+水平 padding 反推,onMeasure 强制 EXACTLY(:184-209)。
setValues会排序后存储(:545/:568)——多 thumb 传入乱序不会崩,但下标语义变。
4. 公开 API 速查
IconSlider(继承链全量):
setThumbText/getThumbText(:276-283)、setTextStart/End(:200-222)、
setHintTextPosition(:307-320,非法值抛 IAE)、setPressAnimEnable(:1086-1088)、
继承 setValue/getValue(Slider.java:119/:99)、setValueFrom/To(BaseSlider:825/:851)、
setStepSize(:988)、addOnChangeListener/addOnSliderTouchListener(:1121/:1144)、
setIgnoreClick/isIgnoreClick(:666-672)、setScrollDragEnable(:3199)、
setPressEnlarge(:3203,GT/Ultra 被 delegate 拦截)。
IconSeekBar:同名 text/hint/pressAnim API(:307-338/:235-248/:1102-1104)+
框架 setOnSeekBarChangeListener/setProgress/setMax/setMin(带 max≠min 校验)。
fromUser 语义:触摸(含 DOWN snap、每个 MOVE、UP 终 snap)= true;程序 set = false。 IconSeekBar 遵循框架 SeekBar 语义。