From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Editor’s Note:n This post has been updated since its initial release. The generate(Bitmap)
and
generate(Bitmap, PaletteAsyncListener)
methods have been deprecated in favor
of a builder static method on the Palette
class. The code listings in this
post now use the new Palette
functionality.
With the relase of Android Lollipop, several new support libraries have been
created. One of the new libraries is for the Palette
class. This new class makes
it easy to extract prominent colors from bitmap images, which is useful if you
want to style other view components to match colors from your image, such as a
background for the image or a text color with suitable contrast. One way I like to use this is to color the ripple drawable behind the
image. It is a subtle effect, but I think it is a nice improvement over the
standard gray. Before you can use it in your projects you need to add the
following compile line to your Gradle dependencies block in your build.gradle
file.
dependencies {
compile 'com.android.support:palette-v7:23.1.1'
}
In order to generate the Palette
object, there is a static from(Bitmap)
method
on the Palette
class.
This method returns a Palette.Builder
object that you can use to tweak the
generated Palette
. Be sure that the Bitmap
you supply to the from()
method is neither null nor recycled, or it will throw an
IllegalArgumentException
.
Once you have the builder, you can use one of the generate methods to create the
Palette
object. One option is to use the no-argument generate()
method like
so.
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.nyancat);
if (myBitmap != null && !myBitmap.isRecycled()) {
Palette palette = Palette.from(myBitmap).generate();
}
This version of the generate method executes synchronously and it should not be
called on your main thread. This method would typically be used when loading
bitmaps on a background thread.
However, sometimes it may not be possible to do this work on the same thread
that you load your images on, so the Palette.Builder
has another generate
method that is configured to do the work asynchronously. This method takes in a
Palette.PaletteAsyncListener
as a parameter. The listener has an
onGenerated(Palette)
method that will be triggered when the Bitmap
processing is finished.
Palette.PaletteAsyncListener paletteListener = new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
// access palette colors here
}
}
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.nyancat);
if (myBitmap != null && !myBitmap.isRecycled()) {
Palette.from(myBitmap).generate(paletteListener);
}
The Palette
object will try to find 16 colors from the image by default, but
there are six color profiles you will use most often:
Here is an example of the main color profiles the Palette
generates:
Once you have the Palette
object, there are built-in getter methods for the six
default color profiles. There is a chance that the Palette
won’t be able to find
a color for a particular profile, so you do need to pass in a default color. The
value returned by these methods is an RGB packed integer.
Palette palette = Palette.generate(myBitmap);
int default = 0x000000;
int vibrant = palette.getVibrantColor(default);
int vibrantLight = palette.getLightVibrantColor(default);
int vibrantDark = palette.getDarkVibrantColor(default);
int muted = palette.getMutedColor(default);
int mutedLight = palette.getLightMutedColor(default);
int mutedDark = palette.getDarkMutedColor(default);
These generated colors come from an object called a Swatch
.
A Swatch object contains some useful data about a color found in the image. You
can get the color in either a RGB packed int or the HSL values. It also includes
the number of pixels of that color in the image (the population). Finally, the Swatch
provides
title and body text colors. These colors are guaranteed to have sufficient
contrast with the Swatch
color so you won’t have issues with legibility.
Palette palette = Palette.from(myBitmap).generate();
Palette.Swatch swatch = palette.getVibrantSwatch();
// Gets the RGB packed int -> same as palette.getVibrantColor(defaultColor);
int rgbColor = swatch.getRgb();
// Gets the HSL values
// Hue between 0 and 360
// Saturation between 0 and 1
// Lightness between 0 and 1
float[] hslValues = swatch.getHsl();
// Gets the number of pixels represented by this swatch
int pixelCount = swatch.getPopulation();
// Gets an appropriate title text color
int titleTextColor = swatch.getTitleTextColor();
// Gets an appropriate body text color
int bodyTextColor = swatch.getBodyTextColor();
For each of the six color profiles, there is another method for getting
its Swatch
object.
Unlike the get color methods, there is no default parameter for the Swatch
methods. If the Palette
object was not able to find a color that matches a
particular color profile the get Swatch
method will just return null.
Palette palette = Palette.from(myBitmap).generate;
Palette.Swatch vibrantSwatch = palette.getVibrantSwatch();
Palette.Swatch vibrantLightSwatch = palette.getLightVibrantSwatch();
Palette.Swatch vibrantDarkSwatch = palette.getDarkVibrantSwatch();
Palette.Swatch mutedSwatch = palette.getMutedSwatch();
Palette.Swatch mutedLightSwatch = palette.getLightMutedSwatch();
Palette.Swatch mutedDarkSwatch = palette.getDarkMutedSwatch();
While the system has convenience methods to get the Swatches
for the six default
color profiles, the other generated swatches need to be retrieved manually.
There is a getSwatches()
method defined on the Palette
that will return a List
of all the swatches created. So if you just want to use the Swatch
represented
by the most pixels, you could just grab it from that list.
Here is an example of the Swatches
the Palette
generates with their respective
populations:
In retrospect, the Palette
object is a cool way to pull out colors from
Bitmap
images. The fact that it comes with built-in generator methods that run
asynchronously make it very quick to use, and I can see the Palette
being used
in a lot of places. To really understand how the Palette
works, I recommend
digging through the source files. It is neat to see how it collects the
different colors in the image, then how it narrows those colors down to the max
color number. I had a lot of fun experimenting with the Palette
, and I look
forward to using it on future projects.
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...