1. 导航标签栏的"颜色"到底分几层
做移动端界面这些年,被问得最多的一类问题就是"标签栏颜色怎么配都不对"。表面看这是个调色问题,实际上它是个分层问题——很多人打开代码就往主色上改,改完发现图标是灰的、选中态只亮了一半、深色模式一片糊,于是认定是组件不好用,其实是漏掉了底下几层。
NavigationTabBar 这种底部导航组件,本质上是把几十个可绘制元素压缩进一条高 48 到 56 dp 的窄条里。窄条意味着每个像素的视觉权重都被放大了,一处颜色没对齐,整条栏看起来就"脏"。所以配色的第一步不是挑颜色,而是先把这条栏拆成可独立控制的层,再逐层给值。
1.1 六个图层:从背景到水波纹
我把一条标准底部导航栏拆成六个颜色层,这个划分在我做过的项目里基本通用:
- 容器层:整条栏的底色。它决定了上方页面内容和下方栏之间的"分界感",是实色、半透明还是带模糊。
- 图标层:未选中和选中两种着色。注意图标本身的填充色会被 tint 覆盖,多色图标在 tint 下会变成单色块。
- 文字层:标签文字的颜色、字重、透明度。文字和图标经常共用一套状态色,但对比度要求不一样。
- 指示器层:选中态的下划线、胶囊背景、圆形光斑、顶部小横条。这是视觉上最"跳"的一层。
- 反馈层:按下时的水波纹、长按的高亮、禁用态的灰化。它只在交互瞬间出现,但缺了会让人觉得"点不动"。
- 分隔层:顶部分隔线、投影、内阴影。这一层最容易被忽略,却直接决定了栏和内容区是不是"贴"在一起。
这六层里,容器层和图标层是必改的,剩下四层是"配好了加分、不配也能跑"。但正是这四层,决定了你的界面是"能用"还是"令人惊艳"。
1.2 只改两层就翻车的原因
最常见的翻车现场是这样的:把容器底色换成品牌色,把选中图标改成白色,跑起来一看——未选中图标几乎看不见了。
原因不难推。假设品牌色是一个中等明度的蓝(比如#2F6BFF),未选中图标如果还是默认的中灰#8A8F99,它在蓝色背景上的对比度只有 1.6:1 左右,肉眼几乎糊成一片。而你如果直接把未选中图标改成白色 100%,选中态再想"更突出"就没有空间了,因为已经到顶了。
这就是分层的意义:容器变深,图标层必须整体提亮;容器变浅,图标层必须整体压深。两层是联动的,不能单独改。同理,指示器层的颜色必须和图标层的选中色拉开距离,否则选中态会"糊"成一个色块,用户看不出哪个是当前页。
还有一个更隐蔽的坑:反馈层的水波纹色如果还是系统默认的深灰,在深色容器上会形成一圈突兀的"污渍",一闪而过但很刺眼。这个问题在测试阶段极难发现,因为要看慢动作才能捕捉到。
1.3 状态组合爆炸的收敛办法
理论上,一个标签有 5 种状态(普通、选中、按下、禁用、聚焦),每个状态又有 6 个颜色层,那就是 30 个颜色值。听着吓人,实际不需要。
我的做法是只把"选中"当作真正的状态分支,其余状态用派生规则算出来。具体来说:
| 状态 | 处理方式 | 理由 |
|---|---|---|
| 普通 | 基准值,手工定义 | 唯一的"锚点",必须精确 |
| 选中 | 手工定义,与普通态成对出现 | 视觉核心,必须精确 |
| 按下 | 选中/普通色叠加 12% 透明度 | 瞬间状态,精度要求低 |
| 禁用 | 普通色降到 38% 不透明度 | 极少出现在底部栏 |
| 聚焦 | 同按下,仅无障碍场景需要 | 可复用按下规则 |
这样需要手工调的值就从 30 个降到 12 个(6 层 × 2 状态),其余全部由规则推导。收敛之后,改一次主色只需要动 2 到 3 个变量,整条栏跟着变,返工率会低很多。
提示:派生规则一定要写成代码里的计算函数,而不是手工填死值。手工填的死值在改主题时必然漏改,漏一个就是一个线上 bug。
2. 配色逻辑:从品牌色推到标签栏色板
拿到一个品牌主色,直接往标签栏上怼,十有八九会出问题。品牌色是为了 logo、按钮、大色块设计的,它的明度和饱和度是按"大面积呈现"调过的。而标签栏是窄条、小图标、密集重复,同样的颜色放上去会显得过重、过艳、过暗。
这一节讲的是怎么把一个"品牌色"翻译成一套"标签栏色板",中间的逻辑必须自己走一遍,不能靠感觉试。
2.1 60-30-10 在窄条上的比例重算
60-30-10 是平面设计里的经典配色比例:60% 主色(通常是中性底)、30% 辅助色、10% 强调色。放到标签栏上,比例需要重算,因为面积分布完全不同。
一条 56 dp 高、全屏宽的栏,容器层占了接近 100% 的面积,但实际上它大部分是"空"的——图标和文字只占很小一块。所以我的经验比例是:
- 容器层 70%:接近中性,允许带一点点色相偏移(比如冷灰偏蓝、暖灰偏棕),但饱和度控制在 8% 以内。饱和度一高,整条栏就在和页面内容"抢戏"。
- 图标/文字层 25%:未选中态用明度差拉开层次,选中态用色相或饱和度的明显跳跃来强调。
- 指示器层 5%:这是唯一允许高饱和的地方。它面积最小、视觉权重最高,所以饱和度和明度都可以推到极限。
很多人翻车就是把 70% 那层做成了高饱和。整条栏变成一块彩色板,页面内容反而成了配角,用户的注意力被持续拉扯,主观感受就是"累"。
2.2 选中态的三种策略与适用场景
选中态怎么和普通态拉开距离,主流有三种做法,各有各的适用面:
策略一:同色相提亮/压深。普通态用主色的 60% 明度版,选中态用 100% 明度版。优点是安全、不会出错、品牌一致性最强。缺点是在深色容器上区分度有限,用户需要盯着看才能分辨。适合工具类、效率类应用,这类用户对界面的容忍度高,不喜欢花哨。
策略二:色相偏移。普通态用中性灰,选中态用主色。这是最通用的方案,区分度极高,一眼就能看出当前页。缺点是对主色的明度有要求——主色太深,在深色容器上就沉下去了;主色太浅,在白底上就飘了。适合内容消费类应用,标签切换频繁,需要快速识别。
策略三:对比色反差。选中态用与主色互补或邻近对比的色相,配合胶囊状指示器。视觉冲击最强,"惊艳"这个词基本靠这条路实现。风险是容易做俗,而且和页面内的其他强调色容易打架。适合品牌调性本身比较年轻、活泼的产品。
我个人的默认选择是策略二,特殊页面(比如运营活动期间)切到策略三。策略一我只在极端保守的场景下用,比如金融、政企类产品。
2.3 对比度红线:把数字算给你看
对比度不是玄学,有明确公式。相对亮度的算法是先把每个通道归一化到 0 到 1,再做 gamma 线性化:
c = 通道值 / 255 c_linear = c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ^ 2.4 L = 0.2126 * R_linear + 0.7152 * G_linear + 0.0722 * B_linear 对比度 = (L_亮 + 0.05) / (L_暗 + 0.05)拿一个具体颜色算一遍。假设主色是#2F6BFF:
- R = 47/255 = 0.1843 → 线性化后 ≈ 0.0284
- G = 107/255 = 0.4196 → 线性化后 ≈ 0.1470
- B = 255/255 = 1.0 → 线性化后 = 1.0
- L = 0.2126×0.0284 + 0.7152×0.1470 + 0.0722×1.0 ≈ 0.1834
白色背景的 L 是 1.0,所以对比度 = 1.05 / 0.2334 ≈4.50:1。
这个数字很关键:4.5:1 正好是小字号文字的合规下限,也就是说#2F6BFF放在白底上属于"刚好及格"。如果你的主色比它再亮一点点,图标和文字就已经不合规了——虽然在设计稿上看不出问题,但在强光下的手机屏幕上会明显发虚。
我的实操红线是:
| 元素 | 最低对比度 | 我的实际取值 |
|---|---|---|
| 未选中图标/文字 | 3.0:1 | 4.0:1 以上 |
| 选中图标/文字 | 4.5:1 | 5.0:1 以上 |
| 指示器与容器 | 1.8:1 | 2.5:1 以上 |
| 分隔线与容器 | 1.2:1 | 1.4:1 左右 |
指示器的对比度要求反而低,因为它是"形状"而不是"信息载体",靠面积和位置就能传达状态。但分隔线要格外小心:太高会形成一条生硬的黑线,太低等于没画。1.4:1 左右通常是最舒服的。