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.
Before we begin – a little background. I didn’t set out to create a library. I was working on some code for a series of Styling Android posts around dynamic UI colouring based upon a ViewPager. While writing this code, I refactored it in to easy to explain components which would easy to write about. Following this refactoring I saw quite a clean API emerging and it was from this that the concept of Prism began to emerge. I showed it to a couple of people whose opinion I value and they agreed that it looked a nice, clean, simple API. I then started the process of turing it in to a library and kept referring back the the API and felt that I was adding lots of power without making the API any more complex. So now the time to release it!
One small note about spelling: As you may have guessed from the spelling of the word ‘colour’ throughout this series of posts I am English and prefer to use the English spelling of ‘colour’. I am fully aware that there are many people who spell it ‘color’. In order to give people the freedom to use whichever spelling they prefer, Prism will accept both spellings. So you can call
setColor(int color) in place of
setColour(int colour) if you prefer. However, using the English spelling of ‘colour’ will result in slightly better performance because there will be one less method call as internally
setColor(int color) simply calls
setColour(int colour). So if you choose to spell ‘colour’ incorrectly you will suffer a very minor performance hit!
For now Prism is divided in to three separate libraries:
- prism – the core Prism library
- prism-viewpager – an extension which hooks up ViewPager with the core Prism library
- prism-palette – an extension which hooks up Palette with the core Prism library
The reasoning behind splitting them up was that the core Prism library has no external dependencies and is small and cheap to add to your project. However both the ViewPager and Palette extensions have external dependencies on the relevant support libraries. So if, for example, your project needs neither of these extensions you do not incur any unnecessary dependencies. However, if you do need ViewPager then you’re project will already have a dependency on the ViewPager support library so the overhead of adding the Prism ViewPager extension lib will be negligible.
Prism is published to both jCenter and Maven Central so, assuming that you already have one or both of these configured as repos for your project it is simply a case of adding the dependencies to your build.grade:
apply plugin: 'com.android.application'
buildToolsVersion "23.0.0 rc3"
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
Once you have added the necessary dependencies you can start using Prism.
The basic concept of Prism is based around three distinct object types: Setters, Filters, & Triggers.
A Setter allows us to set the colour of a UI object – typically a View but not always, as we shall see. The basic function of a Setter is to map a generic
setColour(int colour) (or
setColor(int color)) call to a specific method call on the View that it wraps. For example, the built in ViewBackgroundSetter will map to
setBackgroundCOLOR(int color). In some cases the Setter can provide different behaviour on different versions of Android – for example StatusBarSetter will have no effect on pre-Lollipop devices because status bar colouring is not supported prior to Lollipop. However it will degrade gracefully (no crashing!) so you can safely use it and allow the Setter implementation to worry about how to apply the colour at runtime.
The standard Setters built in to Prism are:
FabSetter(FloatingActionButton fab)– sets the background tint colour on the design support library implementation of FloatingActionButton
StatusBarSetter(Window window)– sets the status bar colour on the supplied Window. Note that this does not take a View.
TextSetter(TextView textView)– sets the text colour on the supplied TextView.
ViewBackgroundSetter(View view)– sets the background colour of the supplied View.
Of course you can create your own Setters for custom Views which may require have additional components which can be coloured, and you can create multiple Setters for a single View to sett different attributes and add them all to the Prism to colour multiple components simultaneously.
A Filter allows us to modify a colour. Prism is generally called with a single colour value and sometimes we may want to apply different variants of that colour to different UI components – Filters enable us to do just that. In its most basic form a Filter takes a colour value as in input, performs some colour transformation upon that colour, and returns the transformed colour value. The basic built-in colour filters are:
IdentityFilter()– returns the colour which was input.
ShadeFilter(float amount)– darkens the colour by effectively mixing in black. The value is a float from 0.0-1.0 which determines how much black to add. A value of 0 makes no change, and a value of 1.0 will produce pure black
TintFilter(float amount)– lightens the colour by effectively mixing in white. The value is a float from 0.0-1.0 which determines how much white to add. A value of 0 makes no change, and a value of 1.0 will produce pure white.
A Trigger is what triggers colour change events. Typically it will call
setColour(in colour) on a
Prism instance to propagate a colour change to all of the Setters which have been registered with that instance.
There are no Triggers currently built in to the core Prism library because that would require some library dependencies. However these are provided in the optional ViewPager and Palette extensions.
Now that we have our three types of components, we need to wire them up and that is what the Prism instance does. Each Prism instance has zero or more Triggers (which will cause a colour change), and one or more Setters (which will be called in response to a colour change). Each Setter can have a Filter attached which will transform the trigger colour before applying it to the Setter.
As well as this basic wiring, Prism also contains some intelligent factories which will construct appropriate Setter implementations automatically for you. For example, if you pass in a design library FloatingActionButton to
Prism#background() it will automatically create a FabColourSetter for you.
A Prism instance is constructed using a builder pattern which constructs the components, wires them up, and then attaches to the Trigger and then responds to trigger events.
Lets look at how we can create a simple Prism instance:
Filter tint = new TintFilter(TINT_FACTOR_50_PERCENT);
prism = Prism.Builder.newInstance()
Most of this code doesn’t require any explanation – it’s standard Android stuff. The highlighted section shows the creation of the Prism instance. We first create a 50% tit filter (which will lighten the colour wherever it is applied.
Next we create a Prism.Builder instance, and add the AppBar instance (which will create a Setter for the AppBar background colour), the Window (which will create a Setter for our StatusBarColour), a TextView (which will set the text colour because of using the
text(TextView) method on the Builder), and a FloatingActionButton which will set the background colour of our FAB with the tint applied.
Finally we call
build() to construct the Prism instance;
Now that we have everything wired up, we can change the colour of all of these components simultaneously by simply calling
setColour(int colour) on the Prism instance:
We also explicitly destroy the Prism instance in onDestroy(). While this isn’t strictly necessary because nothing will hold a reference to Prism instance after the Activity is destroyed leaving it free to be garbage collected, it never hurst the clean up when we no longer need it.
So there we have the fundamentals of Prism: By adding an additional 6 lines of code to our onCreate() we can do this if we hook up our FAB to toggle colours:
So that’s pretty powerful stuff but we haven’t even looked at incorporating Triggers in to the equation yet. In the next article we’ll do just that.
© 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.