Playing with elevation in Android đ„§ (part 2)
In the first part of this article we saw the new APIs introduced in Android Pie that allow us to have coloured elevation shadows. Our ambition had been crushed by the cruel design of elevation shadows APIs, which impose abysmal values for the alphas, thus making even the most lively shadow a pale disappointment.
Is that all there is to it? Turns out, no: I figured out a way around it.
Lateral thinking
It turns out that, while the ambientShadowAlpha
and spotShadowAlpha
values are built into a theme at build time and cannot be changed at runtime, we do have full control over the values we pass tosetOutlineAmbientShadowColor
and setOutlineSpotShadowColor
. Those are full ARGB colours, so if only we could get rid of the additional factor coming in from ambientShadowAlpha
and spotShadowAlpha
, we could fully control the shadows!
Although thereâs no Java/Kotlin API to change them, those are still attributes we can set on the theme, and they act as a multiplication factor. What we want is to set the values to 1.0
so that the only actual factor left â besides the light source which is fixed and does not influence the shadowsâ colour muchâis the alpha channel on the outline shadow colours we set. Bingo!
We just need a couple other tricks to avoid breaking shadows for pre-Pie devices; in particular, we need a ânormalâ base theme that doesnât have any overrides for the shadow alphas (which are available since Lollipop), and then override the alpha values for API 28+, setting them to 1.0
.
Lastly, we set a default shadow colour for API 28+ that has the default alpha values premultiplied in. This way all the shadow will still look like they normally would, but we can still override the colour, and alpha, where we want.
Colours for everyone
We can now have multicoloured shadows, if we so wish, that actually look coloured! There are interesting potential applications for these hacks, such as having some shadows in the app being used to highlight elements, or to obtain particular effects; for example, Mike Wolfson suggested matching an imageâs extracted palette colours with the elevation shadow.
But we can also get crazy and crank up the tackiness to 11! Here we loop the shadowâs hue values with only a fully opaque ambient shadow:
And this is how it looks with 20% alpha for the ambient shadow and 78% for the spot shadow:
Note how you can now control the opacity of the shadows at runtime too, by just varying the alpha component of the shadow colours, and the translationZ
:
Donât try this at home
Weâve said that the light source position for the spot light source is fixed at (0dp, 0dp, 600dp)
, and the light has an 800dp
radius, but can that be changed? Not that there is literally any good reason to do it⊠but just because itâs fun. You should really never, ever do this.
The values encoding the position and size of the light are defined in AOSP by three resources (there is no hardcoded x position for the light, it is centered on the screen):
This means we can theoretically adopt a similar approach as for the alpha, and modify the theme weâre using to override any of those values⊠except it is not that simple. The lightY
, lightZ
and lightRadius
attributes are private, which means that just overriding them in the theme like this:
You will get a build error:
FAILURE: Build failed with an exception.* What went wrong:
Execution failed for task ':app:processDebugResources'.
> Android resource linking failed
~/.gradle/caches/transforms-1/files-1.1/appcompat-1.0.0.aar/bce56009c441dccbe9afec2c071dd480/res/values/values.xml: AAPT: error: style attribute 'android:attr/lightY' is private.
error: failed linking references.
Luckily enough, you can bypass the AAPT private resource validation by prepending an *
to the name of the attribute:
And youâll end up with an atrocity like this:
No, itâs not upside down. Itâs just that the light source is now positioned 1000dp
down the Y axis, which goes towards the bottom of the screen. So yeah, we proved it can be done (for science! đšâđŹ), but you should really really really never do it.