在Android开发中,Material Design的色彩体系并不是只需要一个颜色。一个规范的主题至少包含colorPrimary、colorPrimaryDark、colorAccent等多个角色,而真正遵循Material准则的项目还需要为每个颜色准备十个色阶(从50到900)来适配不同的界面层级和状态。当我们手里只有一个基准色时,如何快速生成一整套符合规范的Material色板,就成了一件既基础又容易被忽视的工作。本文将从色彩体系的构成、现成的拾取器工具,以及自己动手用代码生成色阶三个层面来聊这个话题。

一、先弄明白Material色彩体系里的各个角色
很多开发者对Material配色的理解停留在“主题色加一个强调色”的阶段,这会导致状态栏、按钮、波纹效果的颜色互相打架。实际上,在Material Design的规范里,一个颜色会被拆解成明确的职能。colorPrimary是应用的品牌主色,通常用在Toolbar、底部导航栏等显著位置;colorPrimaryDark是主色的深色变体,在Android 5.0以上用于状态栏背景;colorAccent则是强调色,用在 FloatingActionButton、Switch、进度条等需要吸引用户注意的控件上。
除了这三个基础角色,Material还要求每个颜色有完整的色阶。色阶从50开始到900结束,50最浅,900最深,500通常就是基准色本身。比如经典的靛蓝,50号是近乎天蓝的浅色,900号则接近深藏青。色阶的意义在于:选中态、按下态、卡片阴影、深色模式背景、图表分类色,都可以从同一套色阶中取用,保证视觉语言统一。如果你只在项目里定义了一个色值,后面做深色模式适配或者Material 3的动态色彩时,会发现处处捉襟见肘。
另外要注意对比度问题。Material规范建议主要文字与背景的对比度至少达到4.5:1。一个颜色本身好看,不代表它上面放白色文字依然清晰。这也是为什么正规的拾取器工具都会附带对比度校验功能,稍后我们会提到。
二、现成的颜色拾取器与色板生成工具
如果只是快速出方案,完全没必要自己写算法。第一款值得推荐的是Material官方的Material Theme Builder,它可以直接从一张图片或一个色值提取出完整的Material 3配色方案,并且导出为XML或Compose的Theme代码,对迁移到Jetpack Compose的项目尤其友好。第二款是经典的Material Palette网站,输入任意十六进制色值后,它会即时生成50到900的十个色阶,并给出配套的文字建议颜色(黑色或白色),生成结果可以直接复制成colors.xml片段。
第三个方案是在线工具Color Tool,它的特色在于可以模拟真实界面布局来预览配色效果,同时实时显示对比度是否达标。如果你需要在App内提供取色功能,比如做一个图片取色或者屏幕取色的工具类应用,则可以考虑开源库,例如支持HSL滑杆和色轮交互的开源拾取器控件,Github上有多个Star数较高的实现可以直接集成。选择工具时建议关注三点:是否基于合理的色彩空间计算、是否提供对比度校验、能否导出Android可直接使用的资源格式。
不过工具虽好,也有局限:大多数在线拾取器的色阶生成算法是黑盒,当品牌色本身明度极端(比如接近纯黑或纯黄)时,自动生成的浅色阶可能偏灰发脏。理解背后的生成原理,才能在工具结果不理想时手动修正。
三、用Kotlin自己实现一个Material色阶生成器
自动生成色阶的核心思路是:把RGB颜色转换到HSL色彩空间,然后只对明度L做单调插值,色相H和饱和度S保持基本不变。这样生成的色阶既保留了基准色的“味道”,又能覆盖从浅到深的完整范围。相比直接在RGB上做线性缩放,HSL插值得到的中间色阶更自然,不会出现发灰的问题。
下面是一个可以直接在Android项目中使用的Kotlin实现,利用Android自带的Color.colorToHSV接口完成转换,生成50到900共十个色阶:
import android.graphics.Color
object MaterialPaletteGenerator {
// Material色阶对应的明度目标值,50最浅,900最深
private val lightnessTargets = mapOf(
50 to 0.96f, 100 to 0.90f, 200 to 0.80f, 300 to 0.70f,
400 to 0.62f, 500 to 0.55f, 600 to 0.47f, 700 to 0.39f,
800 to 0.31f, 900 to 0.24f
)
/**
* 根据基准色生成Material色阶
* @param baseColor 例如 0xFF3F51B5.toInt()
* @return 色阶编号到颜色值的映射
*/
fun generate(baseColor: Int): Map<Int, Int> {
val hsv = FloatArray(3)
Color.colorToHSV(baseColor, hsv)
val hue = hsv[0] // 色相保持不变
val saturation = hsv[1] // 饱和度保持不变
return lightnessTargets.map { (level, lightness) ->
// 将HSV的明度(V)直接作为目标明度使用,得到该色阶颜色
level to Color.HSVToColor(floatArrayOf(hue, saturation, lightness))
}
}
/** 计算某颜色上放置文字的建议色:白字或黑字 */
fun suggestTextColor(backgroundColor: Int): Int {
val hsv = FloatArray(3)
Color.colorToHSV(backgroundColor, hsv)
return if (hsv[2] > 0.6f) Color.BLACK else Color.WHITE
}
}使用方式很简单,假设基准色是品牌主色0xFF3F51B5,调用generate就能拿到十个色阶,然后按需写入colors.xml:
<resources>
<color name="indigo_50">#E8EAF6</color>
<color name="indigo_500">#3F51B5</color>
<color name="indigo_700">#303F9F</color>
<!-- colorPrimary取500,colorPrimaryDark取700 -->
</resources>这里有个实践建议:colorPrimary一般对应500号色阶,colorPrimaryDark(状态栏)用700号,深色模式下的主色可以用300号或400号替代,整体协调性会明显好于自己随手调的深色。同时,suggestTextColor给出的文字色建议只是基于明度的粗略判断,如果要严格满足无障碍标准,建议再补一个WCAG对比度计算函数,按照相对亮度公式算出精确比值,低于4.5时自动调整色阶明度,这样生成的色板在任何场景下都能保证文字可读。
总结一下,Material配色这件事,工具能解决八成问题,剩下两成需要理解色彩空间的原理去微调。把上面的生成器集成进开发流程,或者干脆写一个Gradle插件在编译期自动生成色阶资源,就能让主题配色从“凭感觉”变成“有据可依”,后续接手的人也能一眼看懂整套颜色的来龙去脉。
Android颜色拾取器Material Design配色Android主题色生成修改时间:2026-09-06 19:14:42