先看一下google官方说明。https://developer.android.com/reference/android/support/v7/graphics/Palette.html从这个介绍页面,我们大致能够明白:这个新增接口比较简单。一个主要构造方法,传一个bitmap对象,剩下的就是get系列方法。另外,为了方便使用,google还特意新增加了一个异步的构造方法,类似imageloader。不过这里友情提醒一下,以下几个方法,返回值有可能是空,所以使用前要做一次参数有效性判断。
/**
* Returns the most vibrant swatch in the palette. Might be null.
*/
public Swatch getVibrantSwatch() {
return mVibrantSwatch;
}
/**
* Returns a light and vibrant swatch from the palette. Might be null.
*/
public Swatch getLightVibrantSwatch() {
return mLightVibrantSwatch;
}
/**
* Returns a dark and vibrant swatch from the palette. Might be null.
*/
public Swatch getDarkVibrantSwatch() {
return mDarkVibrantSwatch;
}
/**
* Returns a muted swatch from the palette. Might be null.
*/
public Swatch getMutedSwatch() {
return mMutedSwatch;
}
/**
* Returns a muted and light swatch from the palette. Might be null.
*/
public Swatch getLightMutedSwatch() {
return mLightMutedColor;
}
/**
* Returns a muted and dark swatch from the palette. Might be null.
*/
public Swatch getDarkMutedSwatch() {
return mDarkMutedSwatch;
}
按理,这么好的接口,应该能够很快上手。可是在demo里发现,好多情况下,这个接口并没有取到我们想要的,也就是我们认为这附图片上最多的颜色。比如一张一眼看上去灰白占主要的图片,返回来的确是红色,或者黄色。从图片上取到的颜色,不再正常的理解范围之内。看来得研究一下源码了。
同这个特性开放的接口一样,这个特性的实现源码也不是太复杂。package android.support.v7.graphics下面总共只有四个文件,其中一个是public class。其余的三个类中,有两个class是能力层代码。具体如下:
Palette.java 对外接口类。提供一些方法的获取,以及Swatch的定义。
ColorUtils.java 业务强相关的工具类。里面都是一些静态方法。
ColorHistogram.java 实现了颜色提取算法的能力类。
ColorCutQuantizer.java 夹在ColorHistogram.java和Palette.java中间的一个封装类。不过这个类里面提供了一套颜色分裂算法。
这里我们先看一下ColorHistogram这个类是怎么实现主色提取的。构造方法里面传了一个像素点颜色值的数组。传入之后,对所有颜色值进行一次排序。在countDistinctColors方法里面给出了颜色的种类,在countFrequencies方法里面给出了每种颜色所占的比例/个数。这个算法很常见,第一次遇到的同学结合google的注释看起来也挺快的。
private static int countDistinctColors(final int[] pixels) {
if (pixels.length < 2) {
// If we have less than 2 pixels we can stop here
return pixels.length;
}
// If we have at least 2 pixels, we have a minimum of 1 color...
int colorCount = 1;
int currentColor = pixels[0];
// Now iterate from the second pixel to the end, counting distinct colors
for (int i = 1; i < pixels.length; i++) {
// If we encounter a new color, increase the population
if (pixels[i] != currentColor) {
currentColor = pixels[i];
colorCount++;
}
}
return colorCount;
}
private void countFrequencies(final int[] pixels) {
if (pixels.length == 0) {
return;
}
int currentColorIndex = 0;
int currentColor = pixels[0];
mColors[currentColorIndex] = currentColor;
mColorCounts[currentColorIndex] = 1;
if (pixels.length == 1) {
// If we only have one pixel, we can stop here
return;
}
// Now iterate from the second pixel to the end, population distinct colors
for (int i = 1; i < pixels.length; i++) {
if (pixels[i] == currentColor) {
// We've hit the same color as before, increase population
mColorCounts[currentColorIndex]++;
} else {
// We've hit a new color, increase index
currentColor = pixels[i];
currentColorIndex++;
mColors[currentColorIndex] = currentColor;
mColorCounts[currentColorIndex] = 1;
}
}
}
到这里一切都是正常。该提取的颜色应该都提取出来了。那只能接着向上看。ColorHistogram构造的结果,做为参数直接丢给了ColorCutQuantizer的构造方法。在这个构造方法里面我们找到了原因: // Now go through all of the colors and keep those which we do not want to ignore
mColors = new int[rawColorCount];
int validColorCount = 0;
for (int color : rawColors) {
if (!shouldIgnoreColor(color)) {
mColors[validColorCount++] = color;
}
}
google按照某个标准,进行了一次颜色筛选。也就是说我们直接用接口拿到的结果,是经过某个特定的业务筛选出来的。如果我们不知道这个业务,我们拿到的颜色当然就不是我们需要的颜色。将这个筛选逻辑去掉之后,拿到的主色,也就是我们需要的,图片里面最多的颜色。
另外从google的官方说明来看,这个接口的速度非常快。在下面这个方法里面,google将图片的大小统一压缩成一个较小的图片,这个应该是处理时间快的一个重要原因:
public static Palette generate(Bitmap bitmap, int numColors) {
checkBitmapParam(bitmap);
checkNumberColorsParam(numColors);
// First we'll scale down the bitmap so it's shortest dimension is 100px
final Bitmap scaledBitmap = scaleBitmapDown(bitmap);
个人觉得接口层还是提供能力比较好。如果这个接口层提供两套接口:1.单单的主色提取能力,2.某套颜色提取算法。这样APP在业务层里面用起来可能会更方便一些。