最近在逛 Android 开发者官网的时候,发现了一个有趣的官方库 Palette,望文生义,就是调色板了,它是 Jetpack UI 组件的一部分。

为什么觉得有趣,因为它跟我们平时做图片时的取色器一样,能够从一张图片中吸取颜色。

这样说又过于笼统,实际上 Palette 是一个类似调色板的工具,能够根据传入的 Bitmap,提取出主体颜色,我们可以把提取的颜色融入到 UI 中,使 UI 风格更加美观融洽。

官方文档中就给出了一个应用场景:

For example, a music app could use a Palette object to extract the major colors from an album cover, and use those colors to build a color-coordinated song title card.

例如,音乐应用可以使用 Palette 对象从影集封面中提取主要颜色,然后使用这些颜色制作颜色协调的歌曲片头字幕。

我打开 EMUI 的系统音乐应用,看到的确有用到相似的效果:

可以看到,该音乐应用随着歌曲的切换,背景和字体颜色都发生了改变,不过它这里的背景似乎并不是从图片中提取颜色,而是将专辑封面放大后添加了一个深色半透明的蒙层,但字体颜色应该是提取自封面的。

话不多说,我们来看看 Palette 的使用。

首先需要导入 Palette 的库:

implementation 'androidx.palette:palette:{version}'

如果项目使用的是 Android Support 库,则该依赖也应切至 Support 版本。

Palette 可以帮助我们从图片中提取多种颜色:

  • Vibrant
  • Vibrant Dark
  • Vibrant Light
  • Muted
  • Muted Dark
  • Muted Light

接下来就需要一张图片,从网络获取或者丢到本地文件夹都可以,我这里选择从本地加载。

使用方法非常简单,只需要将图片转换成 Bitmap,接下来的一切交给 Palette

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lake_urmia);
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
    @Override
    public void onGenerated(Palette palette) {
        if (palette != null) {
            Palette.Swatch vibrantSwatch = palette.getVibrantSwatch();
            if (vibrantSwatch != null) {
                vibrant.setBackgroundColor(vibrantSwatch.getRgb());
                vibrant.setTextColor(vibrantSwatch.getBodyTextColor());
            }
            Palette.Swatch darkVibrantSwatch = palette.getDarkVibrantSwatch();
            if (darkVibrantSwatch != null) {
                vibrantDark.setBackgroundColor(darkVibrantSwatch.getRgb());
                vibrantDark.setTextColor(darkVibrantSwatch.getBodyTextColor());
            }
            Palette.Swatch lightVibrantSwatch = palette.getLightVibrantSwatch();
            if (lightVibrantSwatch != null) {
                vibrantLight.setBackgroundColor(lightVibrantSwatch.getRgb());
                vibrantLight.setTextColor(lightVibrantSwatch.getBodyTextColor());
            }
            Palette.Swatch mutedSwatch = palette.getMutedSwatch();
            if (mutedSwatch != null) {
                muted.setBackgroundColor(mutedSwatch.getRgb());
                muted.setTextColor(mutedSwatch.getBodyTextColor());
            }
            Palette.Swatch darkMutedSwatch = palette.getDarkMutedSwatch();
            if (darkMutedSwatch != null) {
                mutedDark.setBackgroundColor(darkMutedSwatch.getRgb());
                mutedDark.setTextColor(darkMutedSwatch.getBodyTextColor());
            }
            Palette.Swatch lightMutedSwatch = palette.getLightMutedSwatch();
            if (lightMutedSwatch != null) {
                mutedLight.setBackgroundColor(lightMutedSwatch.getRgb());
                mutedLight.setTextColor(lightMutedSwatch.getBodyTextColor());
            }
        }
    }
});

效果如下:

Palette 解析颜色效果

可以看到,Palette 从图片调色板中也解析到了对应的 6 组 Swatch,每组 Swatch 中包含了多种颜色,同时也提供了获取最终颜色的方法,其中 getPopulation() 表示样本中的像素数量,getRgb()getHsl() 分别是获取颜色的 RGB 值和 HSL 值,getBodyTextColor()getTitleTextColor() 则分别是该颜色下标题和正文所适合的字体颜色。

这里提几个需要注意的点。

一是注意判空,可以看到上面的示例代码中有多处判空,首先是回调得到的 Palette 有可能为空,所以要做判空,同时通过 Palette 解析得到的 Swatch 也可能为空,因为并不是所有图片的色彩都足够丰富以得到这 6 种 Swatch,如果不判空则可能会导致应用崩溃。

二是异步,Palette 除了上方示例的通过 Palette.PaletteAsyncListener() 回调的方法外,还有一个无参的 generate() 方法,不难知道知道分别是异步和同步生成。为什么需要异步,是因为图片过大或者色彩过于丰富的情况下解析有可能会阻塞线程,所以更倾向于使用异步的方式,当然你也可以自行构建异步逻辑。

参考内容