Bootstrap

android 获取图标主要颜色作背景色

功能描述:制作一个应用列表,每个元素如下,内圈是应用的图标,外圈大的圆角长方形作为背景。需要根据应用图标,获取主要的颜色来设置背景色。

获取图标主要颜色的算法,网上很多,获取图标的bitmap,遍历统计数量,如下:

    private static int getMainColor(Drawable dra) throws IOException {
        Bitmap bitmap = drawable2Bitmap(dra);

        int dominantColor;
        int maxCount = 0;

        // 用于存储颜色计数
        HashMap<Integer, Integer> colorCount = new HashMap<>();

        // 遍历图片中的每个像素
        for (int y = 0; y < bitmap.getHeight(); y++) {
            for (int x = 0; x < bitmap.getWidth(); x++) {
                int pixelColor = bitmap.getPixel(x, y);
                int red = Color.red(pixelColor);
                int green = Color.green(pixelColor);
                int blue = Color.blue(pixelColor);
                // 将颜色转换为一个整数
                int color = Color.argb(255, red, green, blue);

                // 更新颜色计数
                //noinspection DataFlowIssue
                int count = colorCount.containsKey(color) ? colorCount.get(color) + 1 : 1;
                colorCount.put(color, count);
            }
        }

        // 保存最大的staticCnt个计数
        for (Map.Entry<Integer, Integer> entry : colorCount.entrySet()) {
            int color = entry.getKey();
            int count = entry.getValue();
//            Log.d(TAG, "---color = " + color + ", count = " + count);
            
            if (maxCount < count) {
                maxCount = count;
                dominantColor = color;
            }
        }
        return dominantColor;
    }

但这样的算法会出现非预期的白色或黑色背景:

原因是这些图标统计下来,白色或黑色就是最多的颜色。通过分析数据,可以看到第二多的颜色与最多的颜色在数量上相差不大。所以需要增加些处理场景。

制定几个基本原则,一般白色不作为背景色,其次是黑色也不太会考虑,对于多种颜色在数量上相近的情况(非白黑),取一个平均值。

算法调整如下:

1、统计前三种最多的颜色,因为最极端情况,就是白色、黑色,还有一个其他颜色。

2、针对白色最多的场景,对后两种颜色取平均值。

3、第二多的颜色为白色,如果第一多为黑色,取第三多的颜色,如果第三多的颜色不存在,则对白色和黑色取均值;如果第一多不为黑色,取第一多的颜色。

4、如果后一种颜色的数量,相比前一种,相差一个数量级以上,则舍去。

5、三种最多的颜色,非黑非白,数量相差不多,取平均值。

private static int getMainColor(Drawable dra) throws IOException {
        Bitmap bitmap = drawable2Bitmap(dra);

        int staticCnt = 3;
        int[] dominantColor = new int[staticCnt];
        Arrays.fill(dominantColor, Color.TRANSPARENT);
        int[] maxCount = new int[staticCnt];
        Arrays.fill(maxCount, 0);

        // 用于存储颜色计数
        HashMap<Integer, Integer> colorCount = new HashMap<>();

        // 遍历图片中的每个像素
        for (int y = 0; y < bitmap.getHeight(); y++) {
            for (int x = 0; x < bitmap.getWidth(); x++) {
                int pixelColor = bitmap.getPixel(x, y);
                int red = Color.red(pixelColor);
                int green = Color.green(pixelColor);
                int blue = Color.blue(pixelColor);
                // 将颜色转换为一个整数
                int color = Color.argb(255, red, green, blue);

                // 更新颜色计数
                //noinspection DataFlowIssue
                int count = colorCount.containsKey(color) ? colorCount.get(color) + 1 : 1;
                colorCount.put(color, count);
            }
        }

        // 保存最大的staticCnt个计数
        for (Map.Entry<Integer, Integer> entry : colorCount.entrySet()) {
            int color = entry.getKey();
            int count = entry.getValue();
//            Log.d(TAG, "---color = " + color + ", count = " + count);
            // 插入排序
            for (int j = 0; j < staticCnt; j++) {
                if (maxCount[j] < count) {
                    for (int k = staticCnt - 1; k >= j + 1; k--) {
                        maxCount[k] = maxCount[k - 1];
                        dominantColor[k] = dominantColor[k - 1];
                    }
                    maxCount[j] = count;
                    dominantColor[j] = color;
                    break;
                }
            }
        }

        StringBuilder out = new StringBuilder("---" + bitmap.getHeight() + "X" + bitmap.getWidth() + ":");
        for (int i = 0; i < staticCnt; i++) {
            out.append(" color: ").append(dominantColor[i]).append(" count: ").append(maxCount[i]);
        }
        Log.d(TAG, out.toString());

        // color为-1是白色,((dominantColor[k] % 0x1000000) == 0x000000)是黑色
        if (dominantColor[1] == -1) {   // 第二多的颜色为白色
            if ((dominantColor[0] % 0x1000000) == 0x000000) {   // 第一多的颜色为黑色
                // 如果第三个颜色存在,直接返回,否则对前两个颜色取均值
                return dominantColor[2] != 0 ? dominantColor[2] : (dominantColor[0] + dominantColor[1]) / 2;
            }
            return dominantColor[0];    // 第一多的颜色不为黑色,直接返回
        }
        if (dominantColor[0] == -1) {   // 第一多的颜色为白色,对后面的颜色取均值
            for (int i = 1; i < staticCnt; i++) {
                dominantColor[i - 1] = dominantColor[i];
                maxCount[i - 1] = maxCount[i];
            }
            staticCnt -= 1;
        }

        int ratio = 20;
        int ret = 0;
        int rCnt = 0;
        for (int k = 0; k < staticCnt; k++) {
            if (dominantColor[k] == 0) {
                continue;
            }
            if (ret != 0 && maxCount[k - 1] > maxCount[k] * ratio) {
                break;
            }
            ret += dominantColor[k];
            rCnt++;
        }
        int last = rCnt > 0 ? ret / rCnt : dominantColor[0];
        Log.d(TAG, "---last: " + last + " rCnt: " + rCnt);
        return last;
    }

整体效果:

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;