Calculate contrast between two colors in Android

Anthony Stéphan
3 min readJun 26, 2018

--

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 calculateContrastmethod 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.WHITE
val 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.

--

--

Anthony Stéphan

Senior Android Developer (Freelance), funder of AS Mobile Development and developer of Printoid for OctoPrint (on Google Play)