Palette / Prism

Prism Fundamentals – Part 3

IMPORTANT: Updates to Prism are on indefinite hold – more details can be found in the README in the within the Prism source. I have decided to continue publishing this short series because it documents how to use Prism in its current form and so still may be useful.

I am extremely excited to announce the release of Prism – an all-new dynamic theming library for Android. This is an initial release to get the basic functionality out there, but it is already pretty powerful. However there are some exciting additions in the pipeline which will make it more powerful still. In this series of articles we’ll cover the various aspects of prism to enable you to make use of it and even extend it yourself to meet the requirements of your project.

Prism LogoPreviously we say how ViewPagerTrigger can easily be hooked up to Prism in order to drive the colouring of our UI quite easily. As well as ViewPager integration, Prism v1.0.0. also has triggering from the Palette support library in an optional module named prism-palette and in this article we’ll look at how we connect this all up.

First we need to add prism-palette as a dependency to our project:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc3"

    defaultConfig {
        applicationId "com.stylingandroid.prism.sample.palette"
        minSdkVersion 7
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:design:22.2.0'
    compile 'com.stylingandroid.prism:prism:1.0.0'
    compile 'com.stylingandroid.prism:prism-palette:1.0.0'
}

PaletteTrigger itself is really simple – we just need to create a new PaletteTrigger instance and add it to the Prism builder:

paletteTrigger = new PaletteTrigger();
prism = Prism.Builder.newInstance()
        .add(paletteTrigger)
        .
        .
        .
        .build();

Once this is done we can trigger colour changes by calling setBitmap(Bitmap bitmap) on the PaletteTrigger. This will create a new Palette instance for the supplied Bitmap and then trigger Prism once the Palette extraction is complete.

However, because of the way that Palette works we need to get a little creative in how we wire up our UI components to get them appropriately coloured. TO under stand this, let’s have a quick recap on how Palette works.

Palette returns up to six separate colour Swatches for vibrant, dark vibrant, light vibrant, muted, dark muted, and light muted colours which have been extracted from the image. For each of these Swatches we can retired one of three colour values: The raw colour, a colour value appropriate for any title text which has a background of the raw colour, and a colour value appropriate for any body text which has a background of the raw colour. So there are up to eighteen different colour variants that we can retrieve from the Palette.

PrismTrigger provides a number of factory methods which return the various Swatches in the form of Filters, and each of these takes a modifier to determine whether the raw, title colour, or body colour will be used. So we are essentially leveraging the Filter mechanism to extract the appropriate colour for each of the UI components that we wire up through Prism.

So, to get a dark vibrant, title text colour we simply chain up the appropriate factory methods to create the filter that we require:

Filter darkVibrantTitle = paletteTrigger.getDarkVibrantFilter(paletteTrigger.getTextFilter()); 

If no filter is set then the vibrant raw colour value will be used, however it is recommended that you wire up what you require. Currently if a particular swatch is not found by Palette then transparency will be applied (effectively hiding the UI component being coloured). This is not ideal and there will be improvements to this in a future release.

So the entirety of our PaletteTrigger is wired up thus:

View vibrant = findViewById(R.id.swatch_vibrant);
View vibrantLight = findViewById(R.id.swatch_vibrant_light);
View vibrantDark = findViewById(R.id.swatch_vibrant_dark);
View muted = findViewById(R.id.swatch_muted);
View mutedLight = findViewById(R.id.swatch_muted_light);
View mutedDark = findViewById(R.id.swatch_muted_dark);

titleText = (TextView) findViewById(R.id.title);
bodyText = (TextView) findViewById(R.id.body);

paletteTrigger = new PaletteTrigger();
prism = Prism.Builder.newInstance()
    .add(paletteTrigger)
    .background(vibrant, paletteTrigger.getVibrantFilter(paletteTrigger.getColour()))
    .background(vibrantLight, paletteTrigger.getLightVibrantFilter(paletteTrigger.getColour()))
    .background(vibrantDark, paletteTrigger.getDarkMutedFilter(paletteTrigger.getColour()))
    .background(muted, paletteTrigger.getMutedFilter(paletteTrigger.getColour()))
    .background(mutedLight, paletteTrigger.getLightMutedFilter(paletteTrigger.getColour()))
    .background(mutedDark, paletteTrigger.getDarkMutedFilter(paletteTrigger.getColour()))
    .background(titleText, paletteTrigger.getVibrantFilter(paletteTrigger.getColour()))
    .text(titleText, paletteTrigger.getVibrantFilter(paletteTrigger.getTitleTextColour()))
    .background(bodyText, paletteTrigger.getLightMutedFilter(paletteTrigger.getColour()))
    .text(bodyText, paletteTrigger.getLightMutedFilter(paletteTrigger.getBodyTextColour()))
    .add(this)
    .build();

This wires up 6 View objects to take each of the vibrant, dark vibrant, light vibrant, muted, dark muted, and light muted swatches, plus two TextViews which will use the vibrant, title text, and light muted, body text Filters respectively.

You may also notice that we’re registering the Activity itself as a Setter. This is to get a callback when the Palette extraction is complete as this can be slow on larger images. This allows us to wait until the Palette extraction is complete before we actually update the image in the ImageView within our layout. This just provides a slightly nicer UX as the image updates at the same time as the UI colours get updated by Prism.

So let’s see this in action (regular readers of Styling Android may recognise Betty who appears occasionally):

So we’re not actually wiring the UI of the app in this case, just giving some examples of how you can extract the various swatch variants and use them. But if you’re read the previous parts of this series it should be fairly straightforward to adapt this.

That concludes our look at the basics, and we’ll finish with Prism for now. However, we’ll re-visit if and when active Prism development resumes.

The full source for this sample is available as one of the sample apps within the Prism source.

© 2015, Mark Allison. All rights reserved.

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

4 Comments

  1. When I use prism in compileSdkVersion 23, it crash

    mPrism = Prism.Builder.newInstance()
    .color(mToolbar)
    .color(mTabLayout)
    .color(mNavHeaderLayout)
    .background(getWindow())
    .color(mFab)
    .build();
    mColor = getResources().getColor(R.color.color_2);
    mPrism.setColor(mColor);

    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.view.View.setBackgroundColor(int)’ on a null object reference
    at com.stylingandroid.prism.setter.ViewBackgroundSetter.onSetColour(ViewBackgroundSetter.java:18)
    at com.stylingandroid.prism.setter.BaseSetter.setColour(BaseSetter.java:26)
    at com.stylingandroid.prism.Prism.setColour(Prism.java:28)
    at com.stylingandroid.prism.Prism.setColor(Prism.java:41)

    1. At least one of the View objects you’re passing in is null. Check that mToolbar, mTabLayout, mNavHeaderLayout, and mFab are not null.

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.