Back in November 2012, I wrote a blog post entitled ”ActionBar on the Move”. This article was mainly dealing with a technique to nicely and uniquely animate your ActionBar. Although I mentioned some of the effect’s possible applications, I never had time to effectively add an ActionBar animation to one of my own apps nor saw an application on the Play Store taking advantage of it.
While being at Google I/O last week, I finally found an application using the ActionBar animation technique. Let’s be honest, it literally blew my mind the first time I saw it. I felt in love with the nice, subtle and yet extremely useful animated effect probably more than the entire app itself! I am pretty sure you know the application I am talking about as it has been presented during the Google I/O keynote. You have also probably recently received an update of it: Play Music!
The latest update of Play Music (v5.0) has been completely redesign and features a brand new artist/album detail screen. If you open such a detail screen, you’ll notice the ActionBar is initially invisible and overlaps a large image describing the artist/album. Once you start scrolling down (if possible), the ActionBar fades in gradually. The ActionBar turns completely opaque when the large image has been scrolled out of the screen.
Here are two main advantages of this ActionBar animation:
Polish the UI: animations synchronized on an element you’re interacting with are generally appreciated by users because it makes them feel the UI is natural and reacts to their actions. The fading animation is a direct consequence of the per-pixel scrolling state and not a launched-once animation.
Take advantage of the screen real estate: while still preserving the UX of the platform, this pattern let the user primarily focus on the content rather than the controls. Used in addition to a nicely designed screen, it can be a game changer for your app’s interface.
In this article, I will deep dive into the details of implementing the technique described in ”ActionBar on the Move” to create an effect similar to the one used in the Play Music app.
In order to better understand the goal we are targeting, you can have a look at the screenshots below or alternatively download the sample application.
As you can easily notice, in order to reproduce such an effect, the ActionBar must overlap the content of the screen. This can be easily done using the android:windowActionBarOverlay XML attributes. The code below describes the definition of the themes we’ll use:
As explained previously, the ActionBar fading is synchronized on the per-pixel state scrolling of the scrolling container. In this example, we’ll simply use a ScrollView as a scrolling container. One of the major drawback of this container is you can’t register a listener in order to be notified when the scroll has changed. This can be easily done be creating a NotifyingScrollViewextending the original ScrollView:
Then, we can use this new scrolling container in an XML layout:
<?xml version="1.0" encoding="utf-8"?><com.cyrilmottier.android.translucentactionbar.NotifyingScrollViewxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/scroll_view"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:id="@+id/image_header"android:layout_width="match_parent"android:layout_height="wrap_content"android:scaleType="centerCrop"android:src="@drawable/daft_punk"/><! -- Some long content --></LinearLayout></com.cyrilmottier.android.translucentactionbar.NotifyingScrollView>
Fading in/out the ActionBar
Now most of the boilerplate is ready, we can plug all of these components together. The ActionBaralgorithm is rather simple and only consists on computing the alpha depending on the current per-pixel scrolling state of the NotifyingScrollView. Note that the effective scrolled distance must be clamped to [0, image_height - actionbar_height] in order to avoid weird values that may occur mainly because of the default over scroll behavior of scrolling containers on Android:
As described in ”ActionBar on the Move”, this snippet of code above doesn’t work for pre-JELLY_BEAN_MR1 devices. Indeed, the ActionBar isn’t invalidating itself when required because it isn’t registering itself as the Drawable’s callback. You can workaround this issue simply be attaching the following Callback in the onCreate(Bundle) method:
You can already run the code “as it”. Although the result looks alike the animation used in Play Music we can still continue to tweak it to make it better.
A final brush stroke
Enforcing ActionBar contrast
Having an transparent ActionBar may lead to design issues because you generally don’t know about the background you’ll be displayed on top of. For instance you may end up with a transparent ActionBar displaying a white text on top of a white description image. No need to say it makes theActionBar invisible and useless.
The easiest way to avoid such a problem consists on modifying the image to make it a little bit darker at the top. Thus, in a worse case scenario (i.e. white image) we would have a grey area on top of the image making the ActionBar content (title, icons, buttons, etc.) visible.
A simple way to do that is to overlay a translucent dark to transparent gradient on top of the image. This can be done in XML only with the Drawable described below:
In Gingerbread (API 9), Android introduced a brand new way to notify the user a scrollable container is being scrolled beyond the content bounds. First it introduced the notion of EdgeEffect (available in the API starting API 14) and enabled over-scroll. While this is not a problem in general, it can be pretty annoying when one of the edge of your scrollable content is different from the background color.
You can reproduce it be simply flinging the ScrollView rapidly to the top and you’ll notice some white color (the background color) appears on top of the screen because the image is scrolling beyond the bounds. I personally consider this a a UI glitch and usually prefer disabling it in this rare cases.
One could imagine the best way to avoid over-scroll is to use View#setOverScrollMode(int) to change the mode to View#OVER_SCROLL_NEVER. Although it works, it also remove the edge effect which can be visually disturbing1. A simple way to do that is to modify the NotifyingScrollView to force the maximum over scroll values to zero when necessary:
I seriously don’t know if the team behind the Play Music application decided to implement the behavior based on my article. But it appears they brilliantly used the technique to both polish and emphasize the UI. It is clearly an awesome pattern to use whenever you need to design a screen which content is self-explanatory and is more important than the ActionBar content itself.
Do not ask me why the naming of the constants/method is so ambiguous…