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.

alpha() - Set the alpha value to be animated to

- Set the alpha value to be animated to scaleX() & scaleY() - Scales the view on it’s X and / or Y axis

& - Scales the view on it’s and / or axis translationZ() - Translates the view on its Z axis

- Translates the view on its Z axis setDuration() - Sets the duration of the animation

- Sets the duration of the animation setStartDelay() - Sets the delay on the animation

- Sets the delay on the animation setInterpolator() - Sets the animation interpolator

- Sets the animation interpolator setListener() - Set a listener for when the animation starts, ends, repeats or is cancelled.

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:

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 .

followed by . 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

- The view to apply the animation on property - The property to animate

- The property to animate start color - The color the animated view starts with

- 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.

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

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.

No Interpolator - The view animates with no rate of change alteration

Fast-Out Linear-In

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

Accelerate - The view gradually accelerates until the animation finishes

Decelerate - The view gradually decelerates until the animation finishes

Anticipate - The view begins by a slight reverse of the stated animation before animating in a standard manner

Anticipate-Overshoot - Similar to Anticipate, but the pull-back motion that occurs during animating is slightly more exaggerated

BounceInterpolator - The view animates a ‘bounce’ effect before coming to a finish

LinearInterpolator - The view to animates from start-to-finish in a linear and smooth motion

OvershootInterpolator - The view animates an exaggeration of the given value, retracting back to the desired value

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)

- 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

- The center co-ordinate for the of the clicked view centerY - The center co-ordinate for the Y-axis of the clicked view

- The center co-ordinate for the of the clicked view targetView - The view to be revealed

- The view to be revealed finalRadius - The radius of the clipping circle, equal to the hypotenuse of our centerX and centerY values

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

- Determines how an activity’s views enter the scene exit - Determines how an activity’s views exit the scene

- Determines how an activity’s views exit the scene reenter - Determines how an activity reenters after previously exiting

- Determines how an activity reenters after previously 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.

All we’ve done here is:

Declare the explode transition

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:

Or programatically:

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:

Here we’ve:

Declared the slide transition

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:

Here we’ve:

Declared the fade transition

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:

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)

Screen 2)

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:

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:

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

- The actual size of the vector image Viewport Height & Width - Declares the size of the virtual canvas which the vector paths are drawn on

- 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

- Declare a group in which the path belongs to Pivot X & Y - Declare the pivot used for the group scale and rotation

- Declare the pivot used for the group scale and rotation Path Fill Color - The fill color of the vector’s path

- 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.

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

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

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

- The property to animate valueFrom - The starting value for the vector path

- The starting value for the vector path valueTo - The target value for the vector path

- The target value for the vector path duration - The duration of the animation

- The duration of the animation interpolator - The interpolator used for the animation

- The interpolator used for the animation valueType - The value type we’re animating

Rotating the shape

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

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!