Exploring Meaningful Motion on Android

At ribot we care about creating beautiful and meaningful experiences for people, in which motion plays a big part.

After seeing an inspiring talk at Droidcon London, I decided to dig deep into motion on Android. From this, I’ve put together my findings to help make both Developers & Designers aware of just how easy it is to add beautiful motion to your Android applications.


Animate!

If you wish to try out these animations for yourself, all of these examples are packaged into an Android app on Github.


I love motion, not only does it boost engagement but it’s instantly noticeable. Think of the apps you use that feature motion design and how pleasing, satisfying, fluent and natural they feel to experience.

Falcon Pro: Even subtle bits of motion can make a huge difference to the user experience

Now compare it to those apps you love that lack those same feelings.

Medium: As much as I love the medium app, it really lacks the motion in the areas that it deserves

Little things make a big difference

We can make use of these motion effects in a number of ways to:

  • Transport users through navigational context
  • Reinforce elemental hierarchy
  • Explain changes between components displayed on screen

The aim of this article is to show you just how easy it is to implement meaningful motion into your applications – so let’s get started.

Touch Feedback

Providing feedback when the user touches the screen helps to communicate in a visual form, that an interaction has been made. These animations should not distract the user, but be enough for them to enjoy, gain clarity from and encourage further exploration.

The Android framework provides ripple states for this level of feedback which can be used by setting a view’s background to one of the following:

  • ?android:attr/selectableItemBackground – Show a ripple effect within the bounds of the view.
The ripple begins at the touch point, filling the background of the view
  • ?android:attr/selectableItemBackgroundBorderless – Show a ripple effect extending the bounds of the view.
A circular ripple effect begins at the touch point, filling a radius extending the bounds of the view

View Property Animator

The ViewPropertyAnimator was introduced at API level 12, allowing you to simply and efficiently perform animated operations (in parallel) on a number of view properties using a single Animator instance.

Here I am animating all of the properties shown below.

Note: When a listener has been set on a view, if you carry out other animations on that same view and don’t wish to use that callback then you must set the listener to null.

It’s both simple and tidy for us to implement programatically:

https://gist.github.com/hitherejoe/0d5097fb4fe6df21ca8b

Note: We don’t actually have to call start() on our animation builder, as the animations automatically start simultaneously once we stop declaring animations. When this is the case, the animations will not start until the next update from the UI toolkit event queue.

Animating the alpha value of the FAB
Animating the scale(X and Y axis) of the FAB
Animating the Z-axis of the FAB

Note: For backwards compatibility, you can use the ViewCompat class to implement the ViewPropertyAnimator from Android API version 4 and up.

Object Animator

Similar to the ViewPropertyAnimator, the ObjectAnimator allows us to perform animations on various properties of the target view (both in code and XML resource files). However, there a couple of differences:

  • The ObjectAnimator only allows animations on a single property per instance e.g. Scale X followed by Scale Y.
  • However, it allows animations on a custom Property e.g. A view’s foreground colour.

Using a custom Property to animate the scaling of a view and change its foreground colour, we can achieve this:

Using our Custom Property, we can create an ObjectAnimator instance by calling ObjectAnimator.ofInt(), where we declare:

  • view – The view to apply the animation on
  • property – The property to animate
  • start color – The color the animated view starts with
  • target color – The color the view should animate to

We then set the evaluator (we use an ArgbEvaluator as we’re animating between color values), set the delay and start() the animation.

https://gist.github.com/hitherejoe/1ba19d055ebab449b820

Next we need to animate the scaling of the view, in which we take a similar approach. The main differences are that:

  • We’re creating an ObjectAnimator instance using ObjectAnimator.ofFloat() because we’re not animating int values when dealing with the size of views
  • Instead of a custom property, we use both the View.SCALE_X and View.SCALE_Y view properties

https://gist.github.com/hitherejoe/92fc2623706a2a52589b

Finally, we need to animate our resized view off the screen. In this case, we use a AdapterViewFlipper to contain our views that we’re animating off screen. Using this means we can call showNext() on the ViewFlipper instance and it’ll handle animating views off the screen using the animation that we’ve defined. Then, the next view is automatically animated on screen, also using an entrance animation we’ve defined.

Interpolators

An Interpolator can be used to define the rate of change for an animation, meaning the speed, acceleration and behaviour during animating can be altered. Several different types of interpolators available and the differences between some of them are subtle, so I suggest trying them on a device.

The view begins animating and finishes in a linear motion
The view begins animating quickly and slows down to a finish
The view begins with a Linear motion and slows down to a finish
The view appears to accelerate at the start of the animation, and gradually decelerates when coming to a finish

Circular Reveal

The CircularReveal animation uses a clipping circle to either reveal or hide a group of UI elements. Other than helping to provide visual continuity, it’s also a pleasant and enjoyable interaction to help gain user engagement.

As shown above, we begin by using a ViewPropertyAnimator to hide the Floating Action Button before beginning the reveal animation on our views. Setting up our circular reveal only requires a few attributes to be defined:

  • startView – The view that the CircularReveal will begin from (ie the pressed view)
  • centerX – The center co-ordinate for the X-axis of the clicked view
  • centerY – The center co-ordinate for the Y-axis of the clicked view
  • targetView – The view to be revealed
  • finalRadius – The radius of the clipping circle, equal to the hypotenuse of our centerX and centerY values

https://gist.github.com/hitherejoe/7aaf961ae916b48dd806

Window Transitions

Customising the transitions used to navigate between activities allows you to produce stronger visual connections between application states. By default we can customise the following transitions:

  • enter – Determines how an activity’s views enter the scene
  • exit – Determines how an activity’s views exit the scene
  • reenter – Determines how an activity reenters after previously exiting
  • shared elements – Determines how shared views transition between activities

As of API level 21, there were several new transitions introduced:

Explode

The explode transition allows views to exit from all sides of the screen, creating an explode effect from the pressed view.

The explode effect works really well for grid based layouts

This effect is simple to implement – to begin with, you need to create the following transition in the res/transition directory.

https://gist.github.com/hitherejoe/8a9c3f998056a6b485c9

All we’ve done here is:

  • Declare the explode transition
  • Set the duration to 300 milliseconds

Next we need to set this as the activity’s transition. We do this by either adding it to our activity theme:

https://gist.github.com/hitherejoe/a22b8e96db4d36b47e86

Or programatically:

https://gist.github.com/hitherejoe/590092a5fddbec3b3922

Slide

The slide transition allows you to slide in / out activities from either the right or bottom of the screen. Although you could previously achieve similar, this new transition is much more flexible.

The slide transition allows us to sequentially slide in child views

This transition is likely to be common when switching activities, I’m particularly fond of the right slide due to its fluid feel. Again, this is easy to create:

https://gist.github.com/hitherejoe/bf42ecfa8ca43c670477

Here we’ve:

  • Declared the slide transition
  • Set the transition’s slideEdge to end (right) so slides perform from the right – a bottom slide would be set to bottom

Fade

The fade transition allows you to transition activities either in or out using a fading effect.

The fade transition is a simple, yet pleasant transition used to fade in views

To create it is even simpler than the previous transitions:

https://gist.github.com/hitherejoe/55a45fb61ec85ed5dfb6

Here we’ve:

  • Declared the fade transition
  • Set the duration to 300 milliseconds

Optimising Transitions

Whilst experimenting I found a couple of approaches that can help improve the transition effects stated above.

Allowing window content transitions – You’ll need to enable the following attribute in themes that inherit from a material theme:

<item name="android:windowContentTransitions">true</item>

Enabling / Disabling Transition Overlaps – When transitioning, there can be a delay when an activity is waiting for another to finish it’s transition before it can start it’s own. Depending on the use case, transitions tend to feel more fluid and natural if you enable these attributes:

<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>

Excluding Views from Transitions – Sometimes we may not want to transition all of our activity’s views. I found that in most cases, the status bar and toolbar caused transition glitches. Luckily, we can exclude specific views from being included in our transition:

https://gist.github.com/hitherejoe/49c9545aeef4354ae2e4

Toolbar and ActionBar – When transitioning between an activity using an ActionBar to one using a toolbar (and vice versa), sometimes I found that the transition wasn’t always smooth. To fix this I ensured that the two activities involved in the transition were using the same component.

Transition Duration – You don’t want to keep the user waiting too long, but you also don’t want components appearing at the speed of light. It depends on the transition that you’re using so it’s best to experiment, but I’ve found that a duration of 200–500 milliseconds works in most cases.

Shared Element Transitions

Shared Element Transitions allow us to animate the transitions between shared views across activities, this creates more pleasurable transitions and gives the user a better sense of their journey.

Here, a view from our first activity scales and translates into the header image in our second activity

In our layouts, we must link any shared views with the use of the transitionName attribute — this states the transitional relationship between the views. Below shows the shared views from the animation above:

These are shared views, which means they’ll animate between each other during activity transition

To transition between these two we begin by declaring the shared transition name, done by using the transitionName attribute in our XML layouts.

Screen 1)

https://gist.github.com/hitherejoe/bd7642546af33a04eecd

Screen 2)

https://gist.github.com/hitherejoe/a2c6b4c09cea063a86ff

Once that’s done, we create a Pair object in activity 1) containing our transitioning view and it’s transitionName. We then pass this to our activity options instance (ActivityOptionsCompat), so both activities are aware of the shared components. From there we start our activity, passing our activity options instance:

https://gist.github.com/hitherejoe/c3a430051c75f8b5b783

Sliding these views in whilst the transition takes place really helps to complete the transition

So that’s the transition between those two views, what about the views in the second activity that slide in from the bottom?

(Those are the guys on the left)

I’m glad you asked! This is also straightforward to achieve, as below:

https://gist.github.com/hitherejoe/6846eaacd352e12e4b70

As you can see, we’re creating a new instance of the Slide transition, adding the target views for the transition and setting the slide as the activity’s entry transition.

Custom Transitions

We also have the ability to create our own custom transitions using any of the animation APIs we’ve looked at so far. For example, we can take Shared Element Transitions one step further to morph transitioning views — this can come in useful when we wish to display dialogs (or similar pop-up views), as shown below:

This motion helps to guide the users attention between the component states

Let’s take a quick look at what’s happening here:

  • We begin by creating a SharedTransition, passing in the pressed view along with the transition name to reference the shared component
  • Next we create an ArcMotion instance, this allows us to create a curved motion effect when transitioning between two views
  • Then we extend ChangeBounds to create a custom transition to morph the two shapes (we have separate class for the button and FAB). Here we override the various methods from the class so that we can animate the required properties. We make use of the ViewPropertyAnimator to animate in the transparency of the dialogs views, the ObjectAnimator to animate between the two views colours and an AnimatorSet instance so that we can animate both of those effects together.

Animated Vector Drawables

As of API Version 21 (Lollipop), an AnimatedVectorDrawable can be used to animate a VectorDrawable’s properties to produce an animated drawable.

It’s now easy to do several different kinds of animations on drawables

But how do we do this? Well, let’s take a look at this one:

Made up of several different files, we begin by creating our two individual vector files which each have several properties:

  • Height & Width – The actual size of the vector image
  • Viewport Height & Width – Declares the size of the virtual canvas which the vector paths are drawn on
  • Group name – Declare a group in which the path belongs to
  • Pivot X & Y – Declare the pivot used for the group scale and rotation
  • Path Fill Color – The fill color of the vector’s path
  • Path Data – Declare the vector path data used to draw the vector

Note: All referenced properties are stored in a general strings file, which helps to keep things nice and tidy.

https://gist.github.com/hitherejoe/e8ef1eb05042971b9d77

The vector generated from our ic_add.xml file (below)

https://gist.github.com/hitherejoe/f7dd17e7df03bdad071d

The vector generated from our ic_remove.xml file (below)

Next we declare the Animated Vector Drawable files which state both the Vector Drawable and the animations used for each drawable state (Add or Remove). Looking at the Add to Remove Animated Vector, we declare a target to:

  • Animate from one state to another
  • Animate the rotation of the drawable

https://gist.github.com/hitherejoe/fdf3df171048705bf451

We then need to create each of the files referenced in those targets.

Changing drawable state

In our add_to_remove.xml we use an ObjectAnimator to morph between the shapes using the following properties:

  • propertyName – The property to animate
  • valueFrom – The starting value for the vector path
  • valueTo – The target value for the vector path
  • duration – The duration of the animation
  • interpolator – The interpolator used for the animation
  • valueType – The value type we’re animating

https://gist.github.com/hitherejoe/ea195376001aad6beed1

Rotating the shape

We take similar approach to rotate the shape, using the rotation property and values instead:

https://gist.github.com/hitherejoe/5b31318d9df7a18d2341

Animating the opposite (from Remove to Add) works the same, just with the animation values reversed.

Our finished Animated Vector Drawable looks great doesn’t it!

To conclude…

Whilst only skimming the surface, I hope this article has provided an insight into how you can create meaningful motion in your applications. I’m looking forward to learning how I can push them further and improve the way my projects look and feel.

If you enjoyed this article, then please hit the recommend button!


I’d love to hear your thoughts and where you’re using these animations – please leave a response or drop me a tweet!

Resources

Here are some resources you may find helpful on this topic:

(A big thanks to the authors!)