Introduction to MotionLayout (part I)

MotionLayout is a new class available in the ConstraintLayout 2.0 library to help Android developers manage motion and widget animation in their application.

The second half (29 min in) of our presentation at Google I/O 2018 on What’s New with ConstraintLayout and Android Studio Design Tools gives a good overview of the capabilities of the library.

In this series of articles we will cover how you can add the library to your application and start experimenting with the concepts and features of MotionLayout.

This part I will cover the basics of MotionLayout :

You can also find the source code for those examples in the ConstraintLayout examples github repository.


Feedback

This library is an early release (alpha 1), and may change before the final stable release. During the alpha release cycle specifically, changes in API / naming are more likely, in reaction to user feedback. Please report any bugs or feature requests on the issue tracker, under the ConstraintLayout component.

Feedback on the APIs is very welcome — do not hesitate to reach out to us!


Why MotionLayout ?

The Android framework already provides several ways of adding animation in your application:

This section will cover how MotionLayout differs from those existing solutions.

MotionLayout, as its name hints at, is first of all a layout, letting you position your elements. It’s actually a subclass of ConstraintLayout, and builds upon its rich layout capabilities.

MotionLayout was created to bridge the gap between layout transitions and complex motion handling. You can think of it in terms of capabilities as a mix between the property animation framework, TransitionManager, and CoordinatorLayout.

A mix between the property animation framework, layout transitions with TransitionManager, and CoordinatorLayout

It lets you describe the transition between two layouts (like TransitionManager), but can animate any properties as well (not just layout attributes). Moreover, it inherently supports seekable transitions, like CoordinatorLayout (the transition can be driven purely by touch and transition to any point of it instantly). It supports touch handling and keyframes, allowing developers to easily customize transitions to their own needs.

MotionLayout is fully declarative

Beyond this scope, the other key difference is that MotionLayout is fully declarative — you can fully describe in XML a complex transition — no code is expected (if you need to express motion by code, the existing property animation framework already provides a great way of doing it).

MotionLayout tooling

We do believe that this focus on declarative motion specification will simplify creating such animations, as well as open the way to provide great graphical tooling in Android Studio.

While we are actively working on this tool, it’s not available yet. It will likely be available once the library reaches stable / beta.

Finally, as a part of ConstraintLayout 2.0, it is available as a support library, backwards compatible to API level 18 (JellyBean MR2) : which means it supports 95% of current Android devices (see distribution chart).

Limitations

MotionLayout will only provide its capabilities for its direct children — contrary to TransitionManager, which can work with nested layout hierarchies as well as Activity transitions.


When to use it

The scenarios we envision for MotionLayout are when you need to move, resize or animate the actual UI elements — buttons, title bar, etc. — elements that the user needs to interact with.

Use MotionLayout when animating UI elements the user will interact with

It’s important to realize that motion is here to serve a purpose — it should not be simply a gratuitous special effect in your application; it should be used to help the user understand what your application is doing. The Material Design principles website is a great introduction to those concepts.

There’s a class of animations that only have to deal with playing predefined content, where the user won’t have — or won’t need — to directly interact with the content. A video, a GIF, or, in a limited way, animated vector drawables or lottie files are generally in this category. MotionLayout doesn’t specifically try to deal with this category of animation (but you can of course include them in a MotionLayout).


Adding MotionLayout to your project

This is done by simply adding ConstraintLayout 2.0 to your Gradle file:

dependencies {
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'
}

Using MotionLayout

MotionLayout is a subclass of ConstraintLayout — as such, you can treat it like a normal layout. To transform an existing ConstraintLayout into a MotionLayout is as easy as replacing the class name from:

<android.support.constraint.ConstraintLayout .../>

to MotionLayout:

<android.support.constraint.motion.MotionLayout .../>

The main difference between ConstraintLayout and MotionLayout at the XML level is that the actual description of what MotionLayout will do is not necessarily contained in the layout file.

Rather, MotionLayout typically keeps all this information in a separate XML file (a MotionScene) that it references, and this information will take precedence over the information in the layout file.

This way, the layout file can contain only the Views and their properties — not their positioning or movement.

ConstraintSets

If you haven’t used ConstraintSets with ConstraintLayout, you may want to take a few minutes to watch this short video from Sean McQuillan (@objcode):

ConstraintSet animation

The general idea with ConstraintSet is that they encapsulate all the positioning rules for your layout; and as you can use multiple ConstraintSet, you can then decide which set of rules to apply to your layout, on the fly, without having to recreate your views — only their position/dimensions will change.

Combined with TransitionManager, this provides a relatively easy way to create animations using ConstraintLayout, as the video above shows.

MotionLayout essentially builds up on this idea, and extends this concept a lot further.

MotionScene

As mentioned, contrary to usual layouts, the specification of what MotionLayout does is kept in a separate XML file, MotionScene, which is stored in your res/xml directory.

A MotionScene file can contain all that is needed for specifying the animation:

  • the ConstraintSets used
  • the transition between those ConstraintSets
  • keyframes, touch handling, etc.

For example, let’s try to move a simple view from one end of the screen to the other, which you can drag with your finger:

First animation

Example 01 : reference existing layouts

With ConstraintLayout, you would create two ConstraintSets — one for the first position (with the widget on the left of the screen), and a second one for the second position (with the widget on the right of the screen).

Start layout:

End layout:

With ConstraintLayout, you could initialize two ConstraintSets out of those two layouts, then apply them (and if using TransitionManager, the transition will be animated). One issue with this approach is that once the transition starts, it’s uninterruptible. You also cannot tell the system to go to a specific point in the transition — meaning you cannot drive the transition via user input.

MotionLayout addresses all those issues. Here’s how you can create the same thing, and even reuse those existing layouts to initialize the two states. First, we’ll create a MotionLayout file for our widget:

Notice that the layout file references a MotionScene file — scene_01

Here it is:

scene_01 specify the default transition, by indicating the start and end ConstraintSet (motion_01_cl_start & motion_01_cl_end, shown as snippets above). Notice as well that we specified an OnSwipe handler for that transition.

And that’s it! the result you will get is the animation shown above.


OnSwipe handler

Looking back at this scene_01.xml file, we specified an OnSwipe handler, contained in the Transition definition. The role of this handler is to let you drive the transition, by matching the motion of your finger.

There’s a few parameters you need to set up:

touchAnchorId: the object we should track (here, @+id/button)
touchAnchorSide: the side of the object that should track your finger (right / left / top / bottom)
dragDirection: the direction of the motion we are tracking (dragRight / dragLeft / dragUp / dragDown will define how the progress value will be set, from 0 to 1)

Example 02 : self-contained MotionScene

The first example gave you a taste on how to quickly set up a MotionLayout, eventually reusing layouts you may already have (if you are currently using ConstraintSets to do basic animation).

MotionLayout also supports describing the ConstraintSets directly in the MotionScene file, located in the res/xml directory. This brings several advantages:

  • a single file to maintain the various ConstraintSets
  • added functionality, handling other visual attributes and custom attributes
  • future-proof: the upcoming MotionEditor in Android Studio will likely only support self-contained MotionScene files.

Interpolated Attributes

Attributes specified in the ConstraintSets elements in a MotionScene file cover more than the usual layout attributes. Beyond position and bounds, the following attributes will automatically be interpolated by MotionLayout:

alpha
visibility
elevation
rotation, rotation[X/Y]
translation[X/Y/Z]
scaleX/Y

Let’s recreate the first example with a self-contained MotionScene file. The MotionLayout file is the same as in example 01, only referencing scene_02.xml :

motion_02.xml

The MotionScene is where things differ; the Transition definition is the same, but we put the definition of the start and end ConstraintSets directly in the file. The main difference from a normal layout file is that we do not specify the type of widgets used here — instead we put the constraints as attribute of a Constraint element.


ConstraintSet

One important thing to keep in mind is how ConstraintSet works — they will replace all existing constraints of the affected widgets.

You thus need to have each Constraint element contain all the constraints you want to apply to the widget. Basically, this is not applying a delta, it is clearing it all and applying only the constraints you specify.

On the other hand, let’s say that you have a layout with a dozen widgets, but only one that you are animating — the ConstraintSet in MotionScene only need to contain the Constraint for that one widget.


MotionLayout attributes for development

MotionLayout itself has a few attributes you may want to specify to help during development:

app:layoutDescription=”reference” has to point to a MotionScene XML file, as we saw earlier
app:applyMotionScene=”boolean” let you apply or not the MotionScene [default=true]
app:showPaths=”boolean” display or not the motion paths [default=false]. Remember to turn off in your production build!
app:progress=”float” let you specify the transition progress from 0 to 1
app:currentState=”reference” let you force a specific ConstraintSet

The End?

This first article covers the basic functionalities introduced in MotionLayout.

You can find the source code for those examples in the ConstraintLayout examples github repository.

There’s a lot more that we will cover in the next few articles:

  • Custom attributes, image transitions, keyframes (part II)
  • Taking advantage of MotionLayout in your existing layouts (CoordinatorLayout, DrawerLayout, ViewPager) (part III)
  • All about Keyframes! (part IV)
  • MotionLayout as a choreographer of root layout
  • Nesting MotionLayout & other Views
  • MotionLayout with fragments

Feedback, feature request, bug reports? please file them on the android issue tracker.

More info on ConstraintLayout & MotionLayout ? Follow us on twitter, @camaelon and @johnhoford