Edit (11/27/12): Added a video demonstrating the result on a Nexus 7 running on Android 4.1
Over the past year, the ActionBar paradigm has become an essential component in the process of designing and developing an Android application. Indeed, the ActionBar has many advantages that help developers in future-proofing their apps. It contains contextual actions, can be customized fairly easily, is highly scalable, etc. Because of this, one should ALWAYS consider using the ActionBar UI pattern in one’s design process when creating a new Android app.
ActionBar
features a lot of interesting styling APIs. These APIs let you brand your application so that it fits your design, while still being recognizable among other applications. Put simply, there are almost no limits to what you can do with an ActionBar
. Until you try doing something more advanced …
Back in March 2012, I was in the process of designing AVélov, I really wanted to have an ActionBar
that differentiated it from the other apps. So I came up with the idea of having an animated ActionBar
background. AVélov being about bikes, I logically wanted the animation to be in relation with bikes (a bike riding from the left edge of the screen to the right screen, a spinning wheel, etc.).
In order to make sure this was possible I created a tiny app with an ActionBar
. I rapidly built an AnimationDrawable
, started it with a simple call to the start()
method and used it as the ActionBar
’s background. The result was pretty disappointing because it wasn’t animating at all. Exploring ActionBarContainer
(a non-public View
backing the ActionBar
) source code I noticed it wasn’t registering a callback1 to my Drawable
:
1 2 3 4 |
|
As a result, the Drawable
had no way to notify the enclosing View
to redraw itself at fixed time intervals. From my point of view this was a wanted behavior to avoid these web-of-the-90’s-red-to-yellow-blinking ActionBar
s. I finally decided to postpone the animation to a future release.
Recently I came back to this feature/enhancement and started developing a new Animatable
Drawable
for testing purposes. This very basic Drawable
changes its color and animates the changes in a smooth fashion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
I think the only interesting thing in this code is the method used to animate a color change. It consists of extracting each color component and animating these values and not the entire color.
I applied this Drawable
to my ActionBar
and boooom it was working! I was quite surprised and starting to investigate. After looking at the AOSP source code for the Jelly Bean MR1 release, I noticed the issue had been fixed by Adam Powell (an engineer at Google working on the UI toolkit) with a7cc06d. The code is now as described below:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The problem with this fix is it wasn’t available for pre-API 17 builds. So I came up with a pretty simple solution for pre-API 17: registering a custom Drawable.Callback
and invalidating the ActionBarContainer
repeatedly setting the same Drawable
with the ActionBar
’s setBackgroundDrawable(Drawable)
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
Thanks to this trick, you can now animate your ActionBar’s background back to API 11 but please keep in mind this may have several and sometimes serious consequences on your application:
It can make your application look different and more polished by featuring tiny, subtle and nice details
When setting an animated background to an
ActionBar
, always make sure it is as subtle as possible. Animations should not distract or interrupt the user in his/her interactions with your app. For instance you could run the animation only when the user is not touching yourActivity
.Using the technique described in this article forces the system to invalidate the whole
ActionBarContainer
for each animation frame. Reduce the duration of your animation as much as possible as it can be CPU & GPU consumingThe animated background
Drawable
should not be something essential to your app.Drawable
should only be considered as styling component and not interaction components.
- I could write an entire book chapter about the
Drawable
notion. Put simple, when setting aDrawable
as aView
background, theView
registers itself as the Drawable’s callback. This let theDrawable
invalidate theView
it is attached to. In other words, it lets you createDrawable
s that can refresh/redraw themselves. Android experts will also say it lets you easily leakContext
s when keeping a static reference to aDrawable
.