范围:组件配色、渐变与遮罩所需的
Colors与UIRenderUtil选型、常量与方法的实际行为,包括lerp的插值通道数、darken对半透明色的处理(含注释与实现不一致的已知疑惑)、alpha()的乘法语义。
前置:核心概念。
Colors 与 UIRenderUtil 的选型
两个类都在 xianfish.fluentui.client.ui.util 包,职责分为颜色计算与渲染辅助两类:
Colors是颜色数学库:语义调色板常量(白、黑、蓝、灰及其透明变体)+ 两个插值/变暗函数。凡是“在两个颜色之间过渡““向黑色压暗“这类纯计算,使用该类。UIRenderUtil是渲染工具类:遮罩/强调色常量(COLOR_前缀)+ 按比例缩放 alpha 的alpha()+ 四通道lerpColor()。凡是“给现成颜色乘一层透明度““渲染层常用的半透明黑“这类渲染辅助,使用该类。
选型规则:业务配色与动画取色使用 Colors;遮罩、压暗与渲染辅助使用 UIRenderUtil。两者都有四通道插值(Colors.lerp 与 UIRenderUtil.lerpColor 行为一致),任一均可;新代码建议统一使用 Colors.lerp。
Colors
常量
| 常量 | 值 | 含义 |
|---|---|---|
WHITE | 0xFFFFFFFF | 不透明白 |
WHITE_50 | 0x80FFFFFF | 50% 白 |
WHITE_20 | 0x33FFFFFF | 20% 白 |
BLACK_50 | 0x80000000 | 50% 黑(标准遮罩) |
BLACK_75 | 0xC0000000 | 75% 黑(深遮罩) |
BLUE | 0xFF3B82F6 | 主题蓝(与 UIRenderUtil.COLOR_BLUE_ACCENT 同值) |
BLUE_50 | 0x803B82F6 | 50% 主题蓝 |
BLUE_DARK | 0xFF1D4ED8 | 深蓝(与 COLOR_BLUE_DARK 同值) |
GRAY_30 | 0x4D808080 | 30% 灰 |
GRAY_50 | 0x80808080 | 50% 灰 |
选择建议:界面语义色(“这是主题蓝”“这是标准遮罩”)按上表名字取;需要 COLOR_ 系的悬停蓝、浅蓝等渲染专用色时,从 UIRenderUtil 常量表获取。
lerp 四通道插值
// 两色之间的匀速插值——渐变、悬停高亮、动画取色都用它
int blended = Colors.lerp(colorA, colorB, t); // t ∈ [0,1],越界由调用方自控
Colors.lerp(a, b, t) 将 ARGB 四个通道各自线性插值后拼回,红、绿、蓝之外,alpha 也参与插值。因此从不透明色插到透明色时,中间帧的透明度是渐变的,不会整段跳变。t 不在函数内钳制:传 0.5f 得中点,负数或大于 1 由调用方的上层逻辑负责。
darken
int darker = Colors.darken(color, 0.3f); // t=0 原样,t=1 纯不透明黑
darken(color, t) 先把 t 钳到 [0, 1],然后执行 lerp(color, 0xFF000000, t),朝不透明的纯黑 0xFF000000 插值。
对带透明度的颜色(alpha 不是 0xFF)调用 darken 时,t 钳 0–1 后朝 0xFF000000 插值;由于 lerp 四通道都插,alpha 也从原值朝 255 变化,因此半透明黑压暗后变得更不透明。方法头注释写的是“向黑色加深(保留透明度不变)“,与实现不符,属已知的注释失真;需要“只压 RGB、锁死 alpha“的语义时,现有 darken 不提供,需自行 mask 或改用其他写法。验证方式:darken(0x80000000, 1f) 返回 0xFF000000(alpha 从 0x80 变 0xFF),与注释字面矛盾,文档与调用以代码行为为准;机制细节与注释实现差异的深挖见工具类。
UIRenderUtil
常量
| 常量 | 值 | 含义 |
|---|---|---|
COLOR_BLACK_SEMI | 0x80000000 | 半透明黑(与 Colors.BLACK_50 同值) |
COLOR_BLACK_75 | 0xC0000000 | 75% 黑(与 Colors.BLACK_75 同值) |
COLOR_BLACK_50 | 0x80000000 | 50% 黑(与 SEMI 同值,两个名字一个数) |
COLOR_BLACK_25 | 0x40000000 | 25% 黑(浅遮罩) |
COLOR_BLACK_12 | 0x1F000000 | ~12% 黑(极浅压暗) |
COLOR_BLUE_ACCENT | 0xFF3B82F6 | 主题蓝(与 Colors.BLUE 同值) |
COLOR_BLUE_ACCENT_SEMI | 0x803B82F6 | 半透明主题蓝(与 Colors.BLUE_50 同值) |
COLOR_BLUE_DARK | 0xFF1D4ED8 | 深蓝(与 Colors.BLUE_DARK 同值) |
COLOR_BLUE_LIGHT | 0xFF60A5FA | 浅蓝(仅此表有) |
COLOR_BLUE_HOVER | 0xFF2563EB | 悬停蓝(仅此表有) |
COLOR_WHITE | 0xFFFFFFFF | 不透明白(与 Colors.WHITE 同值) |
COLOR_WHITE_SEMI | 0x80FFFFFF | 半透明白(与 Colors.WHITE_50 同值) |
选择建议:遮罩梯度(12/25/50/75)与悬停/浅蓝这类渲染态色从本表取;与 Colors 同值的条目可在两处任选,但不得在两处写入不同的数值;同名语义以本表与 Colors.java 当前源码为准。
alpha
// 给现成颜色乘一层透明度——淡出、遮罩渐隐用它
int semi = UIRenderUtil.alpha(0xFF3B82F6, 0.5f); // 不透明蓝 → alpha 变 0x80
alpha(color, factor) 将颜色当前的 alpha 乘以 factor 后写回,RGB 不变。对不透明色,0.5f 得半透明;对已经半透明的色,效果为“再乘一层“(0.5 的一半是 0.25),不是“设成 50%“;需要设定语义时由调用方算好 factor 或改用常量。方法参数名 alpha 易被理解为“设为该 alpha”,实际按乘法理解:alpha() 的实现体即 Math.round(alpha * ((color >> 24) & 0xFF)),确为乘积。工具类机制的更多细节见工具类。
lerpColor
lerpColor(from, to, t) 与 Colors.lerp 行为一致:ARGB 四通道各自插值后拼回(内部 lerpComponent 即 round(a + (b-a)*t))。新代码使用 Colors.lerp;修改其中一处的通道算法时,另一处必须同步修改,否则文档与运行时会出现互相矛盾的插值结果。
不变量
lerp与lerpColor都是四通道含 alpha。任何“只插 RGB“的改法都破坏颜色动画的透明度渐变。darken的行为按代码不按注释:t钳 0–1、向0xFF000000四通道 lerp,半透明输入 alpha 会变大;注释“保留透明度不变“与实现不一致是已知疑惑,调用方若需锁 alpha 必须自行处理。alpha()是乘法缩放,对半透明输入是叠乘不是设值。- 两篇常量真值同源:两张表逐行核对过
Colors.java与UIRenderUtil.java全文;工具类的机制深挖可各留真值表,但取值不得与这两张表矛盾,修改源码常量时两篇同步。
连带影响
- 改
Colors.lerp的通道数或拼装顺序:波及所有颜色动画(developer.Animation.md的示例、Button/ItemView的悬停插值、一切lerp(normal, hover, t)调用点),透明渐变会跳变或错位。 - 改
darken的目标色或钳制:波及所有压暗调用;若把目标从0xFF000000改成保留 alpha 的写法,注释与实现的已知疑惑消失,但既有依赖“压暗同时变实“的视觉要复测。 - 改任一常量字面值:波及该常量的全部引用组件;同值双名(如
BLACK_50与COLOR_BLACK_SEMI)只改一边会造成两处遮罩深浅不一致;修改时需在全库搜索该十六进制值。 - 改
alpha()的乘法语义:波及淡出/遮罩叠乘的组件,把乘法改成设值会让半透明输入的叠乘效果消失。 - 只改
lerp或只改lerpColor:两套插值行为分裂,组件按所用工具分裂为两种行为,两边必须同改(不变量 4)。