将colorPrimary转换为colorPrimaryDark(要比原来更暗多少)

20

根据Material Design的指导,状态栏应比操作栏更暗多少?在运行时我有一个针对操作栏的颜色设置,但在编程时无法知道这个颜色,那么如何获取正确的状态栏颜色呢?

我知道可以使用以下方法使颜色变暗:

this.getSupportActionBar().setBackgroundDrawable(new ColorDrawable(colorPrimary));                              
float[] hsv = new float[3];
Color.colorToHSV(colorPrimary, hsv);
hsv[2] *= 0.8f;
int colorPrimaryDark = Color.HSVToColor(hsv);

if(Build.VERSION.SDK_INT>=21)
    Chat.this.getWindow().setStatusBarColor(colorPrimaryDark);

但是我不确定要把它调暗多少

6个回答

13
材料设计色板不是通过在HSV中操纵颜色生成的,而是使用HSL(色调、饱和度、亮度)完成的。
这里有一个实用类,可以使用HSL来使颜色变暗/变亮。
package com.ammar.materialcolorizer;

import android.graphics.Color;

/**
 * A utility class for darkening and lightening colors in the same way as
 * material design color palettes
 * Created by Ammar Mardawi on 12/4/16.
 */

public class ColorUtil {

    /**
     * Darkens a given color
     * @param base base color
     * @param amount amount between 0 and 100
     * @return darken color
     */
    public static int darken(int base, int amount) {
        float[] hsv = new float[3];
        Color.colorToHSV(base, hsv);
        float[] hsl = hsv2hsl(hsv);
        hsl[2] -= amount / 100f;
        if (hsl[2] < 0)
            hsl[2] = 0f;
        hsv = hsl2hsv(hsl);
        return Color.HSVToColor(hsv);
    }

    /**
     * lightens a given color
     * @param base base color
     * @param amount amount between 0 and 100
     * @return lightened
     */
    public static int lighten(int base, int amount) {
        float[] hsv = new float[3];
        Color.colorToHSV(base, hsv);
        float[] hsl = hsv2hsl(hsv);
        hsl[2] += amount / 100f;
        if (hsl[2] > 1)
            hsl[2] = 1f;
        hsv = hsl2hsv(hsl);
        return Color.HSVToColor(hsv);
    }


    /**
     * Converts HSV (Hue, Saturation, Value) color to HSL (Hue, Saturation, Lightness)
     * Credit goes to xpansive
     * https://gist.github.com/xpansive/1337890
     * @param hsv HSV color array
     * @return hsl
     */
    private static float[] hsv2hsl(float[] hsv) {
        float hue = hsv[0];
        float sat = hsv[1];
        float val = hsv[2];

        //Saturation is very different between the two color spaces
        //If (2-sat)*val < 1 set it to sat*val/((2-sat)*val)
        //Otherwise sat*val/(2-(2-sat)*val)
        //Conditional is not operating with hue, it is reassigned!
        // sat*val/((hue=(2-sat)*val)<1?hue:2-hue)
        float nhue = (2f - sat) * val;
        float nsat = sat * val / (nhue < 1f ? nhue : 2f - nhue);
        if (nsat > 1f)
            nsat = 1f;

        return new float[]{
                //[hue, saturation, lightness]
                //Range should be between 0 - 1
                hue, //Hue stays the same

                // check nhue and nsat logic
                nsat,

                nhue / 2f //Lightness is (2-sat)*val/2
                //See reassignment of hue above
        };
    }

    /**
     * Reverses hsv2hsl
     * Credit goes to xpansive
     * https://gist.github.com/xpansive/1337890
     * @param hsl HSL color array
     * @return hsv color array
     */
    private static float[] hsl2hsv(float[] hsl) {
        float hue = hsl[0];
        float sat = hsl[1];
        float light = hsl[2];

        sat *= light < .5 ? light : 1 - light;

        return new float[]{
                //[hue, saturation, value]
                //Range should be between 0 - 1

                hue, //Hue stays the same
                2f * sat / (light + sat), //Saturation
                light + sat //Value
        };
    }
}

根据Material Design Color Generator,生成primaryColorDark需要加深颜色12。以下是完全按照Material Design Color Generator生成完整调色板的方法:
    setColor("50", ColorUtil.lighten(color, 52), mTv50);
    setColor("100", ColorUtil.lighten(color, 37), mTv100);
    setColor("200", ColorUtil.lighten(color, 26), mTv200);
    setColor("300", ColorUtil.lighten(color, 12), mTv300);
    setColor("400", ColorUtil.lighten(color, 6), mTv400);

    setColor("500", ColorUtil.lighten(color, 0), mTv500);

    setColor("600", ColorUtil.darken(color, 6), mTv600);
    setColor("700", ColorUtil.darken(color, 12), mTv700);
    setColor("800", ColorUtil.darken(color, 18), mTv800);
    setColor("900", ColorUtil.darken(color, 24), mTv900);

10

要精确知道颜色应该变暗多少,一种方法是使用Material Color Tool。只需输入主颜色的十六进制值,它就会为您生成浅色和深色版本。


3
我当时正在寻找以编程方式实现它的方法,因为我是从可能是任何东西的图像中生成主键。不过还是谢谢,这看起来是一个不错的资源。 - Hamzah Malik

6
谷歌建议在应用程序中使用500种颜色作为主要颜色,其他颜色作为强调颜色。工具栏和较大的颜色块应使用应用程序的主要颜色的500种颜色。因此,primaryColor应该是tint 500。
状态栏应该是您的主要颜色的较暗700种色调。因此,primaryColorDark应该是tint 700。
所以我猜primaryColorDark应该比primaryColor暗200个色调。

https://www.google.com/design/spec/style/color.html


好的。但是如果我知道500的颜色代码,我怎么通过编程将其转换为700呢? - Hamzah Malik
如果您有颜色为500的色调,可以使用RGB如下:R0.7,G0.7,B*0.7来获得我认为是色调700的颜色。 - shkschneider
500和700是调色板,但他已经选择了颜色,可能不是500。您并没有严格遵循最初的规则。 - webo80
1
指南不是规则。原文:选择一种颜色,使用0.5和0.7的RB比率来获得漂亮的、MD友好的渐变效果。其余部分由您自行决定。 - shkschneider
1
将RGB值乘以0.7并不是一种一致的变暗颜色的方法,他们也没有通过这种方式从500个颜色中推导出700个颜色。根据你要变暗的颜色,有些颜色会比其他颜色更加变暗。更好的方法是将RGB转换为HSL颜色空间,然后根据你想要变暗的程度从亮度值中减去相应的值,然后再转换回RGB。 - eski

5
官方的Material Color Tool使用JS库chroma.js计算变暗/变亮的颜色,使用的函数为chroma.darken()/chroma.brighten(),其算法基于CIELAB颜色空间。这些函数在Github上的源代码片段中可以找到。
Color.prototype.darken = function(amount=1) {
  const me = this;
  const lab = me.lab();
  lab[0] -= LAB_CONSTANTS.Kn * amount;
  return new Color(lab, 'lab').alpha(me.alpha(), true);
}

Material Color Tool只是简单地使用amount参数的回退值,而LAB_CONSTANTS.Kn为18,则从给定基础颜色的亮度分量 L*中减去18(变暗)/加上18(变亮)。


0

在我看来,"更暗"应该是由你自己决定的。

如果你正在使用Android Studio,在colors.xml中,双击颜色预览,切换到HSV模式,然后降低亮度。(类似于你在程序中所做的操作)


我应该将其降低多少呢? - Hamzah Malik
由你决定...选择你更喜欢的,与你的主要选择相得益彰的颜色组合。也许在这种情况下你不应严格遵循指南,因为建议并不适用于所有颜色。 - webo80
1
好观点,指导方针并不是规则。但这并没有真正回答问题。 - shkschneider
@shkschneider 感谢您的正确答案。我认同我的回答基于个人看法。 - webo80

0

Android Java 代码:

public static int darkenColor(int color) {
    int alpha = Color.alpha(color);
    int red = Color.red(color);
    int green = Color.green(color);
    int blue = Color.blue(color);

    return Color.argb(alpha, (int) (red * 0.9), (int) (green * 0.9), (int) (blue * 0.9));
}

public static String intColor2String(int intColor) {
    String strColor = String.format("#%06X", 0xFFFFFF & intColor);
    return strColor;
}

如何使用?

String dakenColor = Utils.intColor2String(
        Utils.darkenColor(Color.parseColor("#21BBA6")));

欢迎来到 Stack Overflow!当您发布主要是代码的答案时,请尽量解释一下。这段代码是如何工作的?这种编程思路有什么优势? - David García Bodego

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接