使用 Palette 让你的 UI 色彩与内容更贴合

2,117 阅读6分钟

版权声明:

本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有。

每周会统一更新到这里,如果喜欢,可关注公众号获取最新文章。

未经允许,不得转载。

一、前言

今天介绍一个 Android 下比较有意思的 Support v7 库,Palette,它翻译过来就是调色板。

Palette 可以从一张 Bitmap 中提取出它突出的颜色,这样我们就可以将提取出来的颜色设置在 App 的固定 UI 中(例如:ToolBar 的背景),使得 UI 页面的整体风格更加的美观和融洽。

比如,对于一些影视类的 App,视频详情页的主题都是视频的海报,那么对于页面背景,我们可以提取视频海报的颜色,设置在背景上,使得效果更佳柔和美观。

Palette 是一个 Support v7 的包,如果使用 Gradle 引入依赖,这里使用最新的 26.+。

compile "com.android.support:palette-v7:26.+"

二、Palette 的使用

Palette 使用起来非常的简单,既然目的是从一个图片中提取颜色,它的步骤就有:

  1. 传递一个 Bitmap,得到一个 Palette。
  2. 通过 Palette 提取需要的颜色。

就是这么简单,如同要将大象放冰箱,需要几步一样清晰。

那么接下来我们先来了解它使用的细节。

2.1 传递 Bitmap 得到一个 Palette

Palette 在旧版本上有一些 generate() 的方法,用于生成一个 Palette 对象,但是在新版本上已经被标记为 @Depercated 了,所以这里不推荐使用。

而在新版上,推荐使用 Palette.Builder 来创建我们的 Palette 对象,我们可以通过 from() 方法使用它。

/p-from.png
/p-from.png

一般我们使用第一个方法即可,直接传递进去一个 Bitmap 对象。得到 Builder 之后,我们还可以配置一些规则,但是一般我们不需要进行额外的(后面会讲到)。再通过 Builder.generate() 即可得到我们需要的 Palette 对象了。

2.2 通过 Palette 提取颜色

Palette 从图片中提取的颜色,有很多选择。这里又涉及到另外一个类,Swatch 。

Palette 可被提取的每个颜色,都被封装成一个 Swatch 对象,用来管理多种颜色。

这些 Swatch 有:

  • DominantSwatch
  • VibrantSwatch
  • DarkVibrantSwatch
  • LightVibrantSwatch
  • MutedSwatch
  • DarkMutedSwatch
  • LightMutedSwatch

其实这些 Swatch,真的不太好解释其意义,唯一特别一点的就是 DominantSwatch ,它是从图片中提取的最突出的颜色。

这些 Swatch 在 Palette 都提供了对应的 getXxx() 方法获得。不过需要注意的是,这些 getXxx() 方法可能会得到一个 null ,因为有些颜色是没有的。

如果只是需要得到一个颜色值,Palette 同时也提供了对应的 getXxxColor() 方法,方便我们使用。

得到 Swatch 对象之后,就可以通过对应的 Swatch 中对应的 Api 获取我们需要的颜色值。

  • getPopulation() :Swatch 中的像素个数。
  • getRgb():颜色的 RGB 值。
  • getHsl():颜色的 HSL 值。
  • getBodyTextColor():对应的文字颜色值。
  • getTitleTextColor():对应的标题文字颜色值。

通常来说,我们只需要通过 getRgb() 获取到对应的颜色设置在背景上,如果背景之上还有文字内容,可以通过 getBodyTextColor() 提取出与背景匹配的文字颜色值,这样可以显得更加的柔和,让文字看起来更清晰和舒服。比如,如果一个深色的背景,为它设置一个默认的深色文字,基本上就看不见了,因为对比对太弱。

2.3 举个例子

到这里,基本上 Palette 的基本 Api 就讲解清楚了,下面举个实际的例子来看看。

这里找了三张 Eason 的海报,用于做 Palette 的 Demo 资源,间隔去替换图片,然后分别提取出对应的颜色和字体颜色,设置在下面按钮的背景上,然后每 3s 切换一张图片。

因为有一些图片,获取的 Swatch 可能会返回 null ,所以这里用了一个比价扎眼的红色,作为错误色。

以下是获取 Swatch 的代码。

/p-changeColor.png
/p-changeColor.png

接下来通过 Swatch 提取我们需要的颜色。

/p-setViewColor.png
/p-setViewColor.png

这里分别获取了需要的颜色以及字体颜色,下面看看运行的效果:

可以看到,确实有一些颜色,被标记成了红色,说明当前图片有获取不到的对应颜色。

三、分析 Palette 的实现

3.1 Palette 的主线逻辑

继续深入看看 Palette 的实现原理,先从主线开始看。

Builder.generate() 开始。

/p-gen.jpg
/p-gen.jpg

从代码中可以看到,在 generate() 中,主线逻辑:

  1. 首先会通过 scaleBitmapDown() 方法,将图片压缩成一个小像素的,等于生成了一个新的 Bitmap 对象,这样有利于内存的管理,并且也减少了计算量。
  1. 然后再通过 mRegion 判断是否只是提取图片的某个区域,默认是完整的图片全部提取,当然也可以对 mRegion 进行配置。
  2. 之后再构造一个 ColorCutQuantizer 对象,使用它的 getQuantizedColors() 方法得到 Swatch。
  3. 使用完前面压缩的 Bitmap 对象之后,再使用 recycle() 将其回收掉。
  4. 最后,通过 Palette 本身的构造函数,去生成一个 Palette 对象,返回出去。

接下来看看比较关键的 ColorCutQuantizer 中的实现逻辑。

/p-quan.jpg
/p-quan.jpg

从代码中可以看到,其中的逻辑还是很清晰的。

  1. 首先通过 quantizeFromRgb888() 方法,将每个像素的颜色进行量化,类似于将每个颜色取一个靠近的设置。举个不恰当的例子,将不同深度的红,都标记成红色。
  2. 再通过 shouldIgnoreColor() 过滤掉不需要的颜色。
  3. 最终获取到的颜色,如果小于等于我们设置的 maxColors,就可以通过 approximateToRgb888() 生成一批 Swatch。
  4. 如果大于 maxColors,就再通过 quantizePixels() 去掉一些杂色。
  5. 无论如何,最终操作的就是这里得到的 mQuantizedColors 对象。

3.2 Swatch 的 Target

所有需要的 Swatch ,都是被 Target 对象所标记。不同的 Swatch 都是通过 Target 中标记的常量值,进行运算,得到行的颜色。

/p-target.png
/p-target.png

3.3 过滤掉不需要的颜色

Palette 可以设置一些我们不需要的颜色,让它们不参与运算。这里的过滤条件,通过 Filter 来设定,并且 Palette 也提供给了一个 DEFAULT_FALTER 来标记默认的过滤颜色。

/p-filter.png
/p-filter.png

可以看到,默认的 Filter 会过滤掉一些靠近黑和白的颜色。

当然,我们也可以自己定义 Filter ,并通过 Palette 中的 addFilter()clearFilters() 来管理它。

/p-filtermethod.png
/p-filtermethod.png

这里存储 Filter 的是一个 ArrayList ,所以我们是可以定义很多个 Filter 加入进去的,它们都会生效。

3.4 设置 MaxColor

在 ColorCutQuantizer 中,被使用的 maxColor ,主要用于标记需要使用的颜色个数。它是可以通过 maximumColorCount() 方法,进行设置的,如果不对其进行设定,默认值为 16。

/p-maxcolor.png
/p-maxcolor.png

理论上来说,这里设置的maxColor 的值越大,运算花费的时间就越长。而越小,可以被选择的色值也就越少。

所以最佳的做法是根据当前 Bitmap 的用途来决定,色彩越丰富的图,设置的 maxColor 越大,即可。不过正常来说也不需要额外的设定,默认的配置就挺好用了。

四、小结

到这里就分析完 Palette 的所有相关的内容,不要仅仅满足使用。实际上看了 Palette 的源码,对色彩的运算,也有了更加深入的了解。

公众号二维码.jpg
公众号二维码.jpg