Picture this scenario: you have an element whose colour is set at runtime and cannot be known at compile time. Assume this is the fella:
You need to show something just besides it, while still making sure it’s still visible. If you use the same colour… well, you won’t be able to tell that there’s something new besides it.
Your first approach could be just using a color from your theme. In this case, if the theme is Holo, then you would use the Holo blue accent:
This doesn’t really work, right? No, it doesn’t.
We’ll need something more… ad hoc. Something like creating variants of the colour to match it. You know, complementary, or just variants. But this is really hard to do when you just have the Red, Green and Blue components of a colour.
A short trip to colourland
Enter colour models. There’s a lot of maths and science behind them, but I’ll be quick here ‘cos you can still just look stuff up on Wikipedia.
A colour model is a way of representing a color.
The colour model you’re using on a daily basis is called RGB — as in, you represent a colour from its values of the Red, Green and Blue components. It is the colour model usually applied when dealing with light (as in lighting up pixels, here). If you add the transparency, you have the ARGB model, where the A stands for Alpha. The ARGB colour model is the one used throughout Android.
Unfortunately, manipulating the brightness, or the hue, or the saturation of a colour when represented as an RGB triplet is kinda hard. What if there was a way to represent a colour that makes that easy? Well, there is. Enter HSV!
H-S-Who?
HSV (not to be confused with his brother, HSL) is a different representation of the same colour information. Instead of using a triplet of primary colour components, it represents a colour using its Hue, Saturation and Value components.
Y’all know what Saturation and Hue are. The only caveat is, Hue is represented as a 360 degrees angle, while S and B are values that range from 0 to 1. Value is… Brightness, simply put. A colour with full value and saturation is the most intense.
From theory to practice
You might see where this is going. Now, you must be thinking, this will involve lots of maths, maybe even matrices — eww, matrices, right?
Well, no, it doesn’t. Luckily enough, the android.graphics
package includes one small nifty class, Color
, which you can use to bend colours to your will. Apart from all the (A)RGB manipulation functions it provides, you can use your new best friends:
Color.colorToHSV(int originalColor, float[] hsv);
and
int rgbColor = Color.HSVToColor(int alpha, float[] hsv);
Now creating complementary colors is easy as pie (just invert the hue!), as is creating slight variants of a colour. Say for example you want to create a darker variant of our original colour to use it in the slice we put besides it. It’s easy, just convert the colour to HSV, multiply the V component by some float factor in the (0, 1) range, and reconvert back into (A)RGB!
Note that converting to/from HSV loses any Alpha information about the colour. In HSV space, the Alpha information is simply not carried around. You can still get the Alpha component of an ARGB colour int by using:
int alpha = Color.alpha(originalColor);
Get the Gist of it all
I’ve written a simple ColorUtils class that does the “heavy” lifting for you. You can find it here:
https://gist.github.com/rock3r/d026ae14cb98fad20bf6
Bitmasking magic
You might have noticed that there is a bit of bitmasks wizardry going on in the *Alpha
methods. Well, that’s the upside of storing colours into integer values as 0xAARRGGBB
. In fact, if you look at the sources for Color
, this is the primary way of dealing with (A)RGB colours. See how they extract the alpha component of a colour:
public static int alpha(int color) {
return color >>> 24;
}
Nifty, huh?
Unfortunately, our new favourite colour mode, HSV, doesn’t allow us to do this, as each component is represented as a float.
One last thing…
If you look at the code for Color.java
you will notice that there are a bunch of methods you don’t actually see in the SDK. These methods include pure Java handling of HSB colors, as opposed to HSV ones which rely on native code.
Why don’t you see them in the SDK? Well because they’re marked as @hide
, which means they are in the framework, but not exported to the SDK android.jar
. You can access them using reflection if you really need to, but that’s a generally bad idea.
You won’t probably ever need them. And if you do, you’ll still probably not want to wait for the “API council” that was supposed to decide on their fate since… well, their initial committing by Romain Guy, back in July 2009.
Thanks to David González and Roberto Orgiu for proofreading ❤️