AsyncTask / Bitmap / ImageView / Palette / Uncategorized

Palette – Part 2

In the previous article we began looking at the new Palette library which was introduced as part of the Android-L developer preview (but will be available in a support library with compatibility back to API 7) which enables us to extract colour information from bitmaps. We got our image loading working so we’l now turn our attention to extracting the colour information from the image

IMPORTANT NOTE: The code in this article has been based upon the Android-L Developer Preview version of the Palette library. In the full release of Palette, which was made along with the Lollipop SDK, there are some API differences and the code in this article will not work with the full release. Please refer to Part 4 of this series (due for publication on 24th October 2014) for details of the API changes, and for details on the updates to this code to work with the full release of Palette.

The first thing that we need to do is update the setBitmap() method in our Activity to trigger the Palette creation:

public void setBitmap(Bitmap bitmap) {
    imageView.setImageBitmap(bitmap);
    Palette.generateAsync(bitmap, PaletteListener.newInstance(this));
}

Although there is a synchronous method call in the Palette class which we could have called within the image loading AsyncTask, it is pretty self explanatory so I have elected to use the asynchronous method instead which takes two parameters: the Bitmap object we wish to extract the palette colours from; and an instance of Palette.PaletteAsyncListener which is a simple callback interface for the AsyncTask which the asynchronous method uses internally. This has been implemented in PaletteListener:

class PaletteListener implements Palette.PaletteAsyncListener {
    private final WeakReference activityWeakReference;

    public static PaletteListener newInstance(PaletteActivity activity) {
        WeakReference activityWeakReference = new WeakReference(activity);
        return new PaletteListener(activityWeakReference);
    }

    PaletteListener(WeakReference activityWeakReference) {
        this.activityWeakReference = activityWeakReference;
    }

    @Override
    public void onGenerated(Palette palette) {
        PaletteActivity activity = activityWeakReference.get();
        if (activity != null) {
            activity.setPalette(palette);
        }
    }
}

The onGenerated() method is called from the onPostExecute() method of the AsyncTask performing the Palette load and we make a Palette setter call on PaletteActivity after checking that it hasn’t been GC’d.

We now need to define the setPalette() method in PaletteActivity:

public void setPalette(Palette palette) {
    if (palette == null) {
        hidePalette();
        return;
    }
    paletteOverlay.setVisibility(View.VISIBLE);
    setSwatch(normalVibrant, palette.getVibrantColor());
    setSwatch(lightVibrant, palette.getLightVibrantColor());
    setSwatch(darkVibrant, palette.getDarkVibrantColor());
    setSwatch(normalMuted, palette.getMutedColor());
    setSwatch(lightMuted, palette.getLightMutedColor());
    setSwatch(darkMuted, palette.getDarkMutedColor());
}

private void hidePalette() {
    paletteOverlay.setVisibility(View.GONE);
}

private void setSwatch(View swatch, PaletteItem colour) {
    if (colour == null) {
        swatch.setVisibility(View.INVISIBLE);
    } else {
        swatch.setVisibility(View.VISIBLE);
        swatch.setBackgroundColor(colour.getRgb());
    }
}

The first thing that we do is check that we actually got a valid Palette (and hide the Palette overlay view if we didn’t). Then we extract the various colours that were detected within the image. Palette provides us with a number of colour variants which enable us to request a colour which meets certain criteria. We can obtain normal, light, and dark variants of either a vibrant colour or a muted colour which is useful for obtaining a colour which will work well within our app. For example, if we know that we have light text and we want to place it on a background with a colour from the image, then we can request one of the dark variants so that our light text will be against a background with sufficient contrast.

By default Palette will attempt to extract 16 colours from the image and pick these variants from them. There are additional methods which enable the number of colours to be specified. Higher values will provide greater accuracy, but will also take longer. It is possible to obtain a list of all of the colours found using the Palette#getPalette() method, but having an uncategorised list is far less useful than requesting colours matching specific criteria.

The colours themselves are represented by PaletteItem objects which provide us with mechanisms to obtain either colour information in either the RGB or HSL colour spaces.

If we now run this an load an image, we can see the colour variants that have been obtained from a given image (this particular image is of Watford FC mascot Harry the Hornet administering his unique brand of first aid to Udinese striker Luis Muriel):

palette1

One thing that may be obvious from looking at the setSwatch() method (which is responsible for setting the colour of one of the colour swatch Views in our layout) is that we have to perform a null check on the PaletteItem. This is important! We are not guaranteed to get all of the colour variants for any given image.

In the next article in this series we’ll look at a selection of images and analyse the colour information that Palette is able to extract for us.

The source code for this article is available here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

1 Comment

  1. Hi,

    I have been following your blogs for sometime now. I have a requirement to develop and android/ios application for managing my library of books. Can anyone here help me with this requirement. Kindly, reply as soon as possible.

    Thanks,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.