This tutorial will walk you through creating your first MotionLayout steps.

Using MotionLayout, you'll learn the following concepts:

Hope you'll enjoy the codelab!

The source code for this codelab can be found on Github.

In this step, you will download the code for the entire codelab and run a simple example app.

Click the following link to download the source code for this codelab:
Download Code Lab sources

The app will display a crazy Emoji and two bottons. You should see something like this:

Feel free to visit us on GitHub.
The first phase is on the master branch.

The most important thing to understand in MotionLayout is its structure.

Positioning views in MotionLayout is divided into two scenarios:

  1. Static views - views you won't animate will use the ConstraintLayout toolset - as MotionLayout is a subclass of it.
  2. Dynamic views - you'll declare these views using MotionLayout, without positioning attributes. Positioning them will be done by an external XML file, called Scene.

When you want to animate a view in your MotionLayout, you will delegate its constraints to your scene file.

A scene is basically where you describe the occurrences of your animations.

Each scene is composed of:

The system will calculate all of these parameters and handle the animation for you.

To summarize:

The first thing you'll want to do is to note where is the scene XML.

You'll do that by adding the layoutDescription parameter to the root view, which just points to the file where the animations are being described.

    app:layoutDescription="@xml/scene_01"

Your view should look as follows:

<android.support.constraint.motion.MotionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/motionLayout_container"
    android:background="#ffffff"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/scene_01"
    tools:context=".MainActivity">

Next, create a filed called scene_01.xml.

Explanation:
In order to take control of an animation, you need to define a MotionScene.
MotionScene is the entity describing the animation. in your case, you'll make a transition from point A to point B on the screen, with a duration of 1 second.

Every point is a ConstraintSet.
The animation will start at @+id/start, and end at @+id/end.

Your scene_01.xml file should look like this:

 <?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">

    </Transition>


    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>

</MotionScene>

Explanation:
Take a look at the <Constraint> of the @+id/start ConstraintSet,

The constraints in @+id/end are the same, with the only difference being the view is attached to the right.

mMotionLayout.transitionToEnd();
mMotionLayout.transitionToStart();

Your onClick listeners should look like this:

    animateToEndButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mMotionLayout.transitionToEnd();
        }
    });

    animateToStartButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mMotionLayout.transitionToStart();
        }
    });

Run your app

Run the app and tap the buttons - the views should animate back and forth, as expected.

Click the following button to download the source code for this chapter:
Download Code Lab sources

The app should look like this:

Pay attention to the following changes:

 <View
        android:id="@+id/main_iv"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:background="@color/colorAccent"
        />
app:layoutDescription="@xml/scene_02"
<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/main_iv"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="8dp"
        motion:layout_constraintBottom_toTopOf="@+id/animate_to_start_bt"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"/>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/main_iv"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginTop="8dp"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>

Give it a shot by playing with the buttons.

Let's go over some attributes that can be changed during the animation.

android:layout_height="164dp"

Run your app

android:alpha="1.0"

Add the following attribute to your end Constraint

android:alpha="0.5"

Run your app

android:rotation="0.0"

Add the following attribute to your end Constraint

android:rotation="-720"

Run your app

android:translationX="0dp"

Add the following attribute to your end Constraint

android:translationX="100dp"

Run your app

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/main_iv"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="8dp"
        motion:layout_constraintBottom_toTopOf="@+id/animate_to_start_bt"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toStartOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#00ff00" />
    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/main_iv"
        android:layout_width="64dp"
        android:layout_height="164dp"
        android:layout_marginTop="8dp"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#ff0000" />
    </Constraint>
</ConstraintSet>

Run your app

A quick word about interpolations.

You can define an interpolator for every <Transition>.

Images have a special set of attributes you're going to try and explore.

Go back to your image view settings :

 <android.support.constraint.utils.ImageFilterView
        android:id="@+id/main_iv"
        android:src="@drawable/icon2"
        android:layout_width="64dp"
        android:layout_height="64dp"/>
app:layoutDescription="@xml/scene_03"
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">

    </Transition>


    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

</MotionScene>

You can also checkout the image_manipulation branch.

MotionLayout can handle the transition without any callbacks in your code. You can add interactivity by specifying relevant options in your XML scene.
Your user will be able to simply swipe the views back and forth to animate them.
This swipe isn't a simple trigger, but also takes acceleration and velocity into account.

Let's try it now.

app:layoutDescription="@xml/scene_04"
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">

    </Transition>


    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

</MotionScene>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/motionLayout_container"
    android:background="#ffffff"
    android:layout_width="match_parent"
    app:layoutDescription="@xml/scene_04"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <android.support.constraint.utils.ImageFilterView
        android:id="@+id/main_iv"
        android:src="@drawable/icon2"
        android:layout_width="64dp"
        android:layout_height="64dp"/>


</android.support.constraint.motion.MotionLayout>
<Transition
    motion:constraintSetEnd="@+id/end"
    motion:constraintSetStart="@+id/start"
    motion:duration="1000">
    <OnSwipe
        motion:touchAnchorId="@+id/main_iv"
        motion:touchAnchorSide="right"
        motion:dragDirection="dragRight" />
</Transition>

Run your app

Up until now, you defined a pair of start and end states, letting MotionLayout transition between them.
You might ask your self, though - what if I want more than two states?

This is where Keyframes are powerful.

The main idea of a Keyframes is to give you a way to manipulate the animation in terms of size, color, rotation etc during its transition from the start to end state.

Next, you'll take an ImageView from bottom to top (centered horizontally), but this time ading a Keyframe that changes the bottom-up linear path into a diagonal path that goes from bottom-end to center-start and then to up-end.

app:layoutDescription="@xml/scene_05"
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">
        <OnSwipe
            motion:touchAnchorId="@+id/main_iv"
            motion:touchAnchorSide="top"
            motion:dragDirection="dragUp"/>
    </Transition>


    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginBottom="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent">

        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginTop="8dp"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

</MotionScene>
<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
</KeyFrameSet>

Run your app

Explanation :

Let's try a more complicated KeyFrameSet.

<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="25"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="1"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="70"
        motion:target="@id/main_iv"/>
</KeyFrameSet>

Run your app

Let's step it up a notch! Aside from KeyFrames, you also have KeyAttribute which lets you play around with basic view attributes such as size, color, rotation etc.

Enlarge your view to twice its size and rotate it -45 degrees at the halfway point of your animation:

<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="25"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="1"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="70"
        motion:target="@id/main_iv"/>

    <KeyAttribute
        android:scaleX="2"
        android:scaleY="2"
        android:rotation="-45"
        motion:framePosition="50"
        motion:target="@id/main_iv" />
</KeyFrameSet>

Run your app

And if we will go wild :

<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="25"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="1"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentX="0"
        motion:framePosition="70"
        motion:target="@id/main_iv"/>
    <KeyAttribute
        android:rotation="-45"
        motion:framePosition="25"
        motion:target="@id/main_iv" />
    <KeyAttribute
        android:scaleX="2"
        android:scaleY="2"
        android:rotation="-245"
        motion:framePosition="50"
        motion:target="@id/main_iv" />
    <KeyAttribute
        android:rotation="-445"
        motion:framePosition="75"
        motion:target="@id/main_iv" />
</KeyFrameSet>

Run your app

You can leverage keyframes to create some cool combinations.

app:layoutDescription="@xml/scene_06"
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">

    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginBottom="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent">

        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginTop="8dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">

        </Constraint>
    </ConstraintSet>

</MotionScene>
<Button
        android:id="@+id/animate_to_start_bt"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginBottom="10dp"
        android:text="Animate To Start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/animate_to_End_bt"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"/>

    <Button
        android:id="@+id/animate_to_End_bt"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginBottom="10dp"
        android:text="Animate To End"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/animate_to_start_bt"/>

Also, in activity_main.xml, add :

private void handleViews() {
    animateToEndButton = findViewById(R.id.animate_to_End_bt);
    animateToStartButton = findViewById(R.id.animate_to_start_bt);
    mMotionLayout = findViewById(R.id.motionLayout_container);


    animateToEndButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mMotionLayout.transitionToEnd();
        }
    });

    animateToStartButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mMotionLayout.transitionToStart();
        }
    });
}

And call handleViews() from onCreate().

<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentY="0.25"
        motion:percentX="0.75"
        motion:framePosition="25"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentY="0.5"
        motion:percentX="0.1"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentY="0.75"
        motion:percentX="0.75"
        motion:framePosition="75"
        motion:target="@id/main_iv"/>
</KeyFrameSet>

You can even go a bit crazier and do the following:

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="4000">
        <KeyFrameSet>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.80"
                motion:percentX="0.80"
                motion:framePosition="2"
                motion:target="@id/main_iv"/>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.60"
                motion:percentX="0.80"
                motion:framePosition="10"
                motion:target="@id/main_iv"/>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.70"
                motion:percentX="1"
                motion:framePosition="15"
                motion:target="@id/main_iv"/>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.80"
                motion:percentX="0.80"
                motion:framePosition="20"
                motion:target="@id/main_iv"/>

            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.80"
                motion:percentX="0.20"
                motion:framePosition="25"
                motion:target="@id/main_iv"/>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.90"
                motion:percentX="0.10"
                motion:framePosition="30"
                motion:target="@id/main_iv"/>
            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="1"
                motion:percentX="0.20"
                motion:framePosition="35"
                motion:target="@id/main_iv"/>


            <KeyPosition
                motion:keyPositionType="parentRelative"
                motion:percentY="0.2"
                motion:percentX="0.20"
                motion:framePosition="40"
                motion:target="@id/main_iv"/>
        </KeyFrameSet>
    </Transition>

Run your app

MotionLayout offers you the ability to transform a linear path between two points to an arch.

app:layoutDescription="@xml/scene_07"
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="1000">
        <OnSwipe
            motion:touchAnchorId="@+id/main_iv"
            motion:touchAnchorSide="top"
            motion:dragDirection="dragUp"/>

    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginBottom="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent">

        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/main_iv"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_marginTop="8dp"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent">

        </Constraint>
    </ConstraintSet>

</MotionScene>

For this property, you'll want to actually see the path.

app:showPaths="true"
motion:pathMotionArc="startHorizontal"

You should end up with the following:

<ConstraintSet android:id="@+id/start">
    <Constraint
        motion:pathMotionArc="startHorizontal"
        android:id="@+id/main_iv"
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:layout_marginBottom="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent">

    </Constraint>
</ConstraintSet>

Run your app

You can also decide that you want your arch to only run for a portion of the path, and not all of it:

motion:layout_constraintStart_toStartOf="parent"
<KeyFrameSet>
    <KeyPosition
        motion:keyPositionType="parentRelative"
        motion:percentY="0.5"
        motion:pathMotionArc="none"
        motion:framePosition="50"
        motion:target="@id/main_iv"/>
</KeyFrameSet>

Run your app

motion:pathMotionArc="flip"

Run your app

The last feature you'll play with today is a tool that lets you monitor your animation manually.
You'll pass the control of the animation to a Seekbar. This way, you can understand exactly what is happening in every frame of your scene, in an interactive way.

You can control the progress of a MotionLayout by the setProgress() method.

<android.support.v7.widget.AppCompatSeekBar
        android:id="@+id/seek"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="10dp"/>
 private void handleViews() {
    mMotionLayout = findViewById(R.id.motionLayout_container);
    SeekBar seekBar = findViewById(R.id.seek);
    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            mMotionLayout.setProgress(i / 100f);
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    });
}

Run your app

Now is your turn :)

You are going to build the following animation: