johanneskueber.com

Getting the color palette of an image with Coil in Android

For part of my applications I wanted to load an image into an activity. Based on the images main colors, I wanted to adjust the UI of the activity. Loading of the image is the easy part. This can be done via a number of libraries. At the time of writing, the most prominent libraries are: Picasso and Glide. Lately however, there is a new library which targets Kotlin specifically: Coil (see coil-kt.github.io/coil/).

I was an avid user of Glide in my projcet before, but I became somehow tired of it (especially GlideModules). And since I am curious I wanted to try something new: lets use Coil.

However, to fit my requirements, I need a way to extract the colors of the loaded images in order to align the UI colors with the colors from the loaded image. Googles term for most prominent colors is Palette. And since the palette functionality is provided by Google Android Platte Library, the only change required is gathering the source bitmap for palette creation. With using Glide, this can be done using a request listener or even a dedicated library. For Coil, I did not find a libray and no request listeners. Instead, Coil offers Transformations.

The Idea

The idea is simple and always the same - no matter which library you are using:

  1. load the image using coil
  2. extract a bitmap via a transformation using coil
  3. create a platte from the extracted bitmap
  4. extract the required colors from the palette

The Code

The following snippet can be placed anywhere in the source code. I currently use it in my abstract base activity class, all activities in my application inherit from. It can therefor directly access parts of the layout and I do not need to pass those reference to the transformation. Another idea would be to place it somewhere separate. All references to the ImageView, the url to be loaded and the layout parameters to be altered would be needed to be passed to the function. Another solution could include returning the palette. As you can see below, I chose dirtiest, but quickest solution.

banner.load(url) {
    transformations(object: Transformation {
        override fun key() = "paletteTransformer"

        override suspend fun transform(pool: BitmapPool, input: Bitmap): Bitmap {

            val p = Palette.from(input).generate()

            toolbar_layout.setStatusBarScrimColor(p.getDarkVibrantColor(0))

            toolbar_layout.setExpandedTitleTextAppearance(R.style.text_title)

            val swatch = p.vibrantSwatch
            if (swatch != null) {
                toolbar_layout.setContentScrimColor(swatch.rgb)
                toolbar_layout.setCollapsedTitleTextColor(swatch.titleTextColor)
            }

            return input
        }

    })
}

Some details:

  • banner.load(url): The coil way of loading an imagel from an url into and ImageView (i.e. banner is an ImageView)
  • object: Transformation{}: I create an object of type Transformation which will handle our intended use case
  • fun key(): A member function of the Type Transformation which I need to override and we assign a unique name to the transformer. This key can contain any information you deem necessary. It must be unique.
  • fun transform(): This is where a normal transformation would do all the hard work of changing the loaded image.
  • Palette.from(input).generate(): The bitmap loaded from the url is used to generate a Palette object.
  • toolbar_layout: The layout is passed from outside of object into the function and is now used as a reference to change the colors accordingly
  • return input: Since we did not alter the input bitmap at all, the original input is passed as the result of the transformation

The Result

As you can see, the change is really short and really simple. In my app, the image is loaded and the colors of the toolbar are set dynamically based on the image shown. Since the color palette for the movies differs, the toolbar color also changes. Star Wars and Captain Marvel were chosen as an example. Their color palette is not hugely different - but you can see the result.

This is what the final product looks like:


stat /posts/android_coil_palette

2019-12-12: Initial publication of the article
2019-12-18: Added sample videos to show the effect of dynamic colors
2019-01-08: Removal of minor typos