Calculate contrast between two colors in Android
Consider an application with a color picker, for example. You let your user to pick a color (it can be white, black, or any color in the rainbow).
He choose WHITE. and you apply this color to the text of one of your TextView. But the background of your Activity is also WHITE.
Of course, your user could not read the text anymore, since it is WHITE on a WHITE background.
So the main question is: how to know when I should adjust my Activity’s background color, according to the color my user picked?
The answer is very simple. Don’t try to do complex computations between the colors by yourself. Use the ColorUtils class from android.
Calculate contrast between colors
val foregroundColor = Color.WHITEval backgroundColor = Color.GREYval contrast = ColorUtils.calculateConstrast(foregroundColor, backgroundColor)
The calculateContrast
method returns a double
value defined by this formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
Then you can easily compare the contrast between your two values using a threshold:
if (contrast > 1.5f) {
// okay, the text color looks fine on a WHITE background
background.setBackgroundColor(Color.WHITE)
} else {
background.setBackgroundColor(Color.BLACK)
}
Here I’ve choosen a threshold of 1.5f
. In my own opinion, it is a nice value to compare contast between a text color and a background color.
You can do your tests with two colors with this great online tool: http://contrast-ratio.com/
With this tip, you can ensure that your user will be always able to read your text, no matter the color of your background!
Easily adjust your colors
In our example, may the adjustment of the background color can break the app’s design. Let’s see how to easily adjust the text color to maintain a correct readability:
fun Int.canDisplayOnBackground(@ColorInt background: Int): Boolean = return ColorUtils.calculateContrast(this, background) > 1.5f
This method simply returns true
if the color can be applied on the text, false
is the contast would not be sufficient.
An easy solution would be to adjust the HSL parameters of your text color (Hue, Saturation, Lightness).
fun Int.adjustLightness(lightnessFactor: Float): Int {
val hsl = FloatArray(3)
ColorUtils.colorToHSL(this, hsl) hsl[2] = hsl[2] * lightnessFactor
return ColorUtils.HSLToColor(hsl)
}
By applying a factor > 1 to the lightness parameter of our color, we increase it. If the factor is < 1 then the lightness is decreased.
You can of course adjust the saturation parameter to adjust the final render of your text color. Increasing the lightness may result to a high-saturated color.
const val LIGHTNESS_FACTOR = 0.9fval textView = [...]
val textColor = Color.WHITE
val backgroundColor = Color.WHITEval adustedTextColor =
if (!textColor.canDisplayOnBackground(backgroundColor)) {
textColor.adjustLightness(LIGHTNESS_FACTOR)
} else {
textColor
}textView.setTextColor(adustedTextColor)
The result will be a light-gray text on a white background, instead of white on white.
Factorization of this code makes the thing still easier:
@ColorInt
private fun Int.adjustForBackground(backgroundColor: Int): Int =
if (this.canDisplayOnBackground(backgroundColor)) this
else this.adjustLightness(LIGHTNESS_FACTOR)
So you can directly adjust the text color like that:
textView.setTextColor(textColor.adjustForBackground(backgroundColor))
Work with the other methods from ColorUtils
ColorUtils
also comes with other useful methods.
calculateLuminance(color: Int)
allows you to get the luminance of a color value.
calculateMinimumAlpha(foregroundColor: Int, backgroundColor: Int, minContrastRatio: Float)
calculates the minimum alpha value which can be applied to foregroundColor that would have a contrast value of at least minContrastRatio when compared to backgroundColor.