- 文档
- 教程
- 知识库
【免费下载链接】android-tech-frontier
【停止维护】一个定期翻译国外Android优质的技术、开源库、软件架构设计、测试等文章的开源项目
本篇文章的主体内容源自本仓库 issue-25/在Android Lollipop中使用Palette抽取Bitmap颜色.md,并参考了同期的 Android主题动态切换开源库Prism基本原理3-搭配Palette使用.md、Support Libraries v22.1.0.md 等关联译文,从 API 使用、底层原理到真实项目落地三个层面展开。
Palette 是随 Android Lollipop 一同发布的 Support 库组件,它可以从一张 Bitmap 中自动抽取若干种"特征颜色",并额外给出与每种特征色搭配得当的标题/正文字体色,非常适合做"界面颜色随图片变化"的动态配色场景。读完本文,你将掌握 Palette 的同步/异步抽取 API、六大主色种类的取色方法、Swatch 的完整数据模型,以及它在真实开源项目(Prism 主题框架、InstaMaterial 概念应用)中的落地方式。
一、Palette 是什么:一张图,一组"看得懂"的颜色
随着 Android Lollipop 发布,一批新的 Support 库诞生,其中就包括palette-v7(Palette)。这个库可以让你很轻松地从一幅图中抽取特征颜色,这在"希望界面的颜色风格适应指定图片"时非常有用——例如根据用户头像、封面图、内容大图来动态改变状态栏、Toolbar、按钮或波纹(Ripple)的颜色。
一个典型的、效果直观的使用场景是定制图像波纹的颜色。这本身只是一个微不足道的视觉细节,但相比默认的纯灰色波纹,它能带来明显的质感提升。
从实现层面看,Palette 会读取图片中的全部像素,通过**色彩量化(Color Quantization)**把颜色深度压缩到很小的色彩数(通常为 16),再从量化结果中挑选出具有代表性的"主色"。这一点在 issue-8/Support Libraries v22.1.0.md 中有明确佐证:生成 Palette 过程中最耗时的一步正是色彩量化。
二、添加依赖:一行 gradle 配置
要在工程中使用 Palette,只需在模块的build.gradle的dependencies中添加:
dependencies { compile 'com.android.support:palette-v7:21.0.0' }作为对比,在 others/InstaMaterial概念设计系列/实现Instagram的Material Design概念设计/readme.md 中,这个社区著名的 Material Design 概念项目同样通过compile 'com.android.support:palette-v7:+'引入 Palette,配合 AppCompat、RecyclerView、CardView 等兼容包使用。需要说明的是,本仓库中的示例基于 2014-2015 年间的 Support 库版本(21.0.0 起),依赖坐标com.android.support:palette-v7沿用至今,只是后续版本号不断升级(21.0.0 → 22.1.0 → …)。
三、生成 Palette:同步与异步两种方式
生成一幅图像的 Palette 有两种方法:
generate(Bitmap):生成含有16 种颜色种类的 Palette;generate(Bitmap, int):生成含有指定数量颜色种类的 Palette,数量越多所需时间越久。
这两个方法是同步方法,由于它们很可能比较耗时(在分析大图片或所需颜色较多时尤其明显),所以不应该在主线程中执行。正确做法是:先在别的线程(如AsyncTask、线程池)中调用这两个函数完成解析,解析成功之后再回到 UI 线程使用结果。
不过,很多时候你并不在加载图片的那个线程(非主线程)里使用解析出的颜色,为此 Palette 提供了异步方法。它们与同步方法的区别在于需要额外传入一个PaletteAsyncListener,作为图片解析完成后的回调:
generateAsync(Bitmap, PaletteAsyncListener)generateAsync(Bitmap, int, PaletteAsyncListener)
PaletteAsyncListener的实现非常简单,只需重写onGenerated即可:
Palette.PaletteAsyncListener listener = new Palette.PaletteAsyncListener() { public void onGenerated(Palette palette) { // 使用Palette对象,获取解析出的颜色 } };这样你就可以在任何需要的时候(例如Activity.onCreate()中发起图片加载的同时)使用这两个函数创建 Palette,回调触发后再更新界面。
四、六大主色种类:从 16 种特征色中筛选的"常用色"
Palette 默认会解析出图像的 16 种特征颜色种类,但以下 6 种是你最经常用到的:
| 颜色种类 | 说明 |
|---|---|
vibrant | 鲜艳色 |
dark vibrant | 鲜艳色中的暗色 |
light vibrant | 鲜艳色中的亮色 |
muted | 柔和色 |
dark muted | 柔和色中的暗色 |
light muted | 柔和色中的亮色 |
这一"6 种主色"的划分同样出现在 issue-25/Android主题动态切换开源库Prism基本原理3-搭配Palette使用.md 中:Palette从图片中抽取 6 种不同的颜色样本Swatch,每种Swatch又能提供初始颜色、标题字体色、正文字体色三个颜色值,所以一共可以从Palette中获取到18 个颜色值。
五、获取六大主色:内置 getter 与默认颜色
拿到Palette对象之后,可以通过内置 getter 函数直接获取这六种颜色。你需要传入默认颜色,以防 Palette 无法解析到指定颜色种类;返回值的类型是 24 位 RGB 颜色数值。
Palette palette = Palette.generate(myBitmap); int vibrant = palette.getVibrantColor(0x000000); int vibrantLight = palette.getLightVibrantColor(0x000000); int vibrantDark = palette.getDarkVibrantColor(0x000000); int muted = palette.getMutedColor(0x000000); int mutedLight = palette.getLightMutedColor(0x000000); int mutedDark = palette.getDarkMutedColor(0x000000);实际开发中,默认颜色通常不要写死为纯黑0x000000,而是传入一个与应用主题协调的兜底色(例如getResources().getColor(R.color.default_color)),这样即使图片中缺少某类颜色,界面也不会出现突兀的纯黑或透明。
六、Swatch:颜色背后的完整数据模型
Palette 解析出的每一个颜色种类都对应一个Swatch对象,Swatch中包含了关于该颜色的丰富信息。你可以从中获取:
- RGB 颜色值:
getRgb(); - HSL 颜色向量:
getHsl(),返回float[3](H 色调、S 饱和度、L 亮度)。这与 issue-7/Android-Support库22.1版.md 中提到的ColorUtils颜色转换能力(如将颜色转换为 HSL 值)是配套的; - 该颜色在图像中所占的像素数:
getPopulation(); - 与对应颜色搭配的标题字体颜色:
getTitleTextColor(); - 与对应颜色搭配的正文字体颜色:
getBodyTextColor()。
其中标题、正文两个字体色与对应颜色的对比度是 Palette 内部处理好的,你不必再去操心可读性问题。示例如下:
Palette palette = Palette.generate(myBitmap); Palette.Swatch swatch = palette.getVibrantSwatch(); //rgb颜色值 int rgbColor = swatch.getRgb(); //hsl颜色向量 float[] hslValues = swatch.getHsl(); //该颜色在图像中所占的像素数 int pixelCount = swatch.getPopulation(); //对应的标题字体颜色 int titleTextColor = swatch.getTitleTextColor(); //对应的正文字体颜色 int bodyTextColor = swatch.getBodyTextColor();获取六个主颜色的Swatch要使用与之前直接取颜色不同的 getter 函数。它们不需要提供默认值——如果 Palette 没有解析到对应的Swatch,会直接返回null:
Palette palette = Palette.generate(myBitmap); Palette.Swatch vibrantSwatch = palette.getVibrantSwatch(); Palette.Swatch vibrantLightSwatch = palette.getLightVibrantSwatch(); Palette.Swatch vibrantDarkSwatch = palette.getDarkVibrantSwatch(); Palette.Swatch mutedSwatch = palette.getMutedSwatch(); Palette.Swatch mutedLightSwatch = palette.getLightMutedSwatch(); Palette.Swatch mutedDarkSwatch = palette.getDarkMutedSwatch();注意:由于Swatchgetter 可能返回null(当图像中不存在该颜色种类时),调用前务必做判空处理。这一点在 Prism 库的实践中也有印证:issue-25/Android主题动态切换开源库Prism基本原理3-搭配Palette使用.md 提到,如果对应的颜色种类在Palette中没有抽取到,PaletteTrigger会使用透明色(即绑定的 UI 组件会在屏幕上暂时消失),这是当时版本的一个已知不理想之处。
七、获取全部 Swatch:16 种颜色种类
Palette 只为上述六种主色种类提供了便捷 getter。如果你想使用其他颜色种类(总共有 16 种),需要手动获取:调用getSwatches()会返回一个列表,里面包含所有获取到的Swatch:
List<Palette.Swatch> swatches = palette.getSwatches();拿到List后,可以遍历每个Swatch,通过getPopulation()比较它们分别在图像中所占的比例,也可以按getPopulation()排序后筛选"占比最大"的主色用于大面积背景。这种"全量遍历 + 自定义筛选"的方式,正是 Palette 在真实项目中灵活运用的基础。
八、性能与 API 演进:从 21.0.0 到 22.1.0
本仓库的 issue-8/Support Libraries v22.1.0.md 记录了 Palette 在 22.1.0 版本中的两个重要变化,有助于你理解这个库的底层机制与选型:
1. 引入 Builder 构建方式。由于 Palette 的"把手"和设置项日益增多,官方引入了Palette.Builder类来帮助获取 Palette 实例。issue-7/Android-Support库22.1版.md 对此有更具体的描述:不再直接调用Palette.generate(Bitmap),而是使用Palette.from(Bitmap)取回一个Palette.Builder实例,在调用generate()或generateAsync()之前,你可以自由修改生成的最大颜色数、设置图像的最大尺寸来重新运行 Palette。典型用法形如:
Palette palette = Palette.from(myBitmap) .maximumColorCount(16) // 设置颜色种类数量 .resizeBitmapArea(10000) // 限制参与分析的像素面积,控制耗时 .generate();2. 大幅提升生成速度。生成 Palette 最耗时的步骤是色彩量化——读取图片中的所有像素点,并把颜色深度降低到一个很小的色彩数(通常是 16)。22.1.0 通过更少的对象分配、更合适的数据结构以及更低的算法复杂度优化了色彩量化性能。文中给出了在 ART 设备上的测试数据:性能大约提升 5~6 倍,Dalvik 设备上效果更明显;ColorUtils也从 Palette 中移入 support-v4,提供计算颜色对比度、最小文字 alpha 值等通用颜色工具。
这说明一个重要的选型结论:Palette.from(Bitmap)+maximumColorCount()/resizeBitmapArea()的组合,是控制"颜色丰富度与解析耗时"这对矛盾的关键旋钮——颜色数量越多、参与分析的像素越多,抽取越精细,耗时越长。
九、真实项目落地:Prism 中的 PaletteTrigger
本仓库 issue-25/Android主题动态切换开源库Prism基本原理1-核心功能.md 与 issue-25/Android主题动态切换开源库Prism基本原理3-搭配Palette使用.md 展示了 Palette 的一种完整工程化封装:主题切换框架 Prism 通过prism-palette扩展库将 Palette 与 UI 染色体系打通。
核心思路是:
- 新建一个
PaletteTrigger实例,加入Prism.Builder; - 调用
PaletteTrigger.setBitmap(Bitmap bitmap)提取目标 Bitmap 的特征颜色(内部会新建Palette实例异步处理,处理结束后触发 Prism 颜色变换); - 通过
PaletteTrigger提供的工厂方法(如getVibrantFilter(getColour())、getLightMutedFilter(getBodyTextColour()))获取对应主色种类的Filter,Filter决定最终给 View 设置的是初始颜色、标题字体色还是正文字体色。
一个完整的绑定示例:
paletteTrigger = new PaletteTrigger(); prism = Prism.Builder.newInstance() .add(paletteTrigger) .background(vibrant, paletteTrigger.getVibrantFilter(paletteTrigger.getColour())) .background(vibrantLight, paletteTrigger.getLightVibrantFilter(paletteTrigger.getColour())) .background(vibrantDark, paletteTrigger.getDarkMutedFilter(paletteTrigger.getColour())) .background(muted, paletteTrigger.getMutedFilter(paletteTrigger.getColour())) .background(mutedLight, paletteTrigger.getLightMutedFilter(paletteTrigger.getColour())) .background(mutedDark, paletteTrigger.getDarkMutedFilter(paletteTrigger.getColour())) .background(titleText, paletteTrigger.getVibrantFilter(paletteTrigger.getColour())) .text(titleText, paletteTrigger.getVibrantFilter(paletteTrigger.getTitleTextColour())) .background(bodyText, paletteTrigger.getLightMutedFilter(paletteTrigger.getColour())) .text(bodyText, paletteTrigger.getLightMutedFilter(paletteTrigger.getBodyTextColour())) .add(this) .build();这段代码将六个View组件分别赋予 Palette 解析后的六种初始颜色,并分别为两个TextView的文本赋予"鲜艳色搭配的标题文本色"与"亮柔和色搭配的正文文本色"。由于 Palette 解析大图片需要一些时间,示例还把 Activity 实现成了Setter(通过.add(this)),在Palette处理完图片后的回调setColour(int colour)中同时设置ImageView的图片,让"解析颜色"与"显示图片"的结果同步呈现到屏幕上。
这正是"Palette 抽取颜色 → 六种主色/Swatch → 标题与正文字体色 → 界面染色"这条完整链路在实际框架中的体现,可以作为你在自己工程中封装取色逻辑的参考范式。
十、使用要点小结
- 主线程限制:
generate(Bitmap)/generate(Bitmap, int)是同步耗时操作,大图或高颜色数量时务必放到工作线程;UI 更新请使用generateAsync系列。 - 默认颜色不可省略:
getXxxColor(defaultColor)系列必须提供兜底色,防止 Palette 解析不到对应颜色种类时界面异常。 - 判空处理:
getXxxSwatch()系列在对应颜色种类缺失时返回null,使用前必须判空。 - 性能旋钮:通过
Palette.from(Bitmap)的 Builder 配置maximumColorCount与resizeBitmapArea,在色彩丰富度与解析耗时之间取得平衡;22.1.0 起色彩量化已大幅优化(ART 设备约 5~6 倍提速)。 - 应用场景:波纹颜色定制、状态栏/Toolbar 动态取色、主题框架(如 Prism 的
PaletteTrigger)的自动染色,以及音乐/图片类 App 的封面配色,都是 Palette 的典型落地位置。
延伸阅读:本仓库内与 Palette 相关的译文还包括 issue-8/Support Libraries v22.1.0.md(Palette 22.1.0 的 Builder 与性能优化细节)、issue-7/Android-Support库22.1版.md(22.1 版本速度提升与
Palette.from()用法)以及 issue-25/Android主题动态切换开源库Prism基本原理3-搭配Palette使用.md(PaletteTrigger的完整源码级用法)。想深入源码的话,可以继续阅读 Palette 所在 Support 库的源码,理解其色彩量化与Swatch生成的具体算法。
- 文档
- 教程
- 知识库
【免费下载链接】android-tech-frontier
【停止维护】一个定期翻译国外Android优质的技术、开源库、软件架构设计、测试等文章的开源项目
相关推荐
Android 主题动态切换开源库 Prism 基本原理(三):搭配 Palette 从图片抽取主题色并联动染色
Android 主题动态切换开源库 Prism 基本原理(三):搭配 Palette 从图片抽取主题色并联动染色 本文是 Prism 主题动态切换框架系列文章的
文档教程知识库5步让旧Mac重获新生:OpenCore Legacy Patcher终极升级指南
5步让旧Mac重获新生:OpenCore Legacy Patcher终极升级指南 想让你的旧款Mac运行最新macOS系统吗?OpenCore Legacy
操作系统固件驱动开发如何快速实现智能主题色提取:Seal的终极颜色算法指南
如何快速实现智能主题色提取:Seal的终极颜色算法指南 Seal是一款基于yt dlp的Android音视频下载工具,其内部集成了强大的主题颜色提取算法,能够从
移动开发音视频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考