MotionLayout

A MotionLayout is a ConstraintLayout which allows you to animate layouts between various states. MotionLayout links to and requires a MotionScene file.

MotionScene

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.

A ConstraintSetlets you create and save constraints, and apply them to an existing ConstraintLayout

Motion scene files usually contain two constraint sets: one for the beginning of the animation and one for the end.

Switching to a MotionLayout

The MotionLayout is a subclass of ConstraintLayout. So to transform a ConstraintLayout to a MotionLayout we need to only replace the class name from :

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

to :

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

Creating the MotionScene

To create our MotionScene we first need to create an XML directory in our resources file. Then we create a new XML file, called motion_scene_main.xml

Our MotionScene should contain a transition attribute where we will specify the starting and ending constraints, how long it should last and we are also going to add a gesture handler.

motion_scene_main.xml
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto">
    <Transition
        app:constraintSetEnd="@layout/activity_main_end"
        app:constraintSetStart="@layout/activity_main_start"
        app:duration="1000">

        <OnSwipe
            app:dragDirection="dragUp"
            app:touchAnchorId= "@id/second_bg"
            app:touchAnchorSide="top" />
    </Transition>
</MotionScene>

If you want to learn more about the gesture handlers check the documentation here.

Connecting the dots

As we mentioned above, the MotionLayout links to the MotionScene. But right now our MotionLayout is still not aware that we created that file. So we need to go back to the activity_main_start.xml file, add a layoutDescription attribute to the widget, and set its value to the name of the motion scene file.

activity_main_start.xml
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="@color/colorBgDark"
    app:layoutDescription="@xml/motion_scene_main">
    
    ....
    
</android.support.constraint.motion.MotionLayout>

OOPS!

If we now try to enter the design mode of that layout we will see that it cannot render. The reason for this is that all the widgets found in the starting constraint should also be included to the ending one and vice versa. So we need to copy paste the views we haven't added so far to both of our layouts.

activity_main_start.xml
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="@color/colorBgDark"
    app:layoutDescription="@xml/motion_scene_main">

    <!-- Circle ImageView --> 
    <!-- Main ImageView --> 
    <!-- CardView --> 
    <!-- ImageView Paw 1--> 
    <!-- ImageView Paw 2--> 
    <!-- ImageView Paw 3--> 
    <!-- ImageView Paw 4--> 
    <!-- ImageView Paw 1--> 
    <!-- Second Background View--> 

    <ImageView
        android:id="@+id/iv_leaf_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="28dp"
        android:layout_marginBottom="150dp"
        android:rotation="80"
        android:src="@drawable/ic_leaf"
        app:layout_constraintBottom_toBottomOf="@+id/guideline_horizontal20"
        app:layout_constraintEnd_toEndOf="@+id/guideline_verticalHalf"
        tools:ignore="ContentDescription" />

    <ImageView
        android:id="@+id/iv_leaf_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="136dp"
        android:layout_marginBottom="150dp"
        android:rotation="15"
        android:src="@drawable/ic_leaf"
        app:layout_constraintBottom_toBottomOf="@+id/guideline_horizontal20"
        app:layout_constraintEnd_toEndOf="parent"
        tools:ignore="ContentDescription" />

    <!-- Guidelines --> 

</android.support.constraint.motion.MotionLayout>
activity_main_end.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorBgDark"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- Circle ImageView -->
    <!-- ImageView Leaf 1-->
    <!-- ImageView Leaf 2-->
    <!-- ImageView Leaf 1-->
    <!-- CardView-->

    <ImageView
        android:id="@+id/iv_main"
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:scaleType="centerInside"
        android:src="@drawable/alex"
        app:layout_constraintBottom_toBottomOf="@+id/second_bg"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.343"
        tools:ignore="ContentDescription" />

    <ImageView
        android:id="@+id/iv_paw_1"
        android:layout_width="65dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="88dp"
        android:rotation="30"
        android:src="@drawable/ic_paw"
        app:layout_constraintStart_toStartOf="@+id/iv_main"
        app:layout_constraintTop_toTopOf="@id/iv_main"
        tools:ignore="ContentDescription" />

    <ImageView
        android:id="@+id/iv_paw_2"
        android:layout_width="65dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="112dp"
        android:layout_marginTop="56dp"
        android:rotation="45"
        android:src="@drawable/ic_paw"
        app:layout_constraintStart_toStartOf="@+id/iv_main"
        app:layout_constraintTop_toTopOf="@id/iv_main"
        tools:ignore="ContentDescription" />

    <ImageView
        android:id="@+id/iv_paw_3"
        android:layout_width="65dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="92dp"
        android:rotation="-30"
        android:src="@drawable/ic_paw"
        app:layout_constraintEnd_toEndOf="@+id/iv_main"
        app:layout_constraintTop_toTopOf="@id/iv_main"
        tools:ignore="ContentDescription" />

    <ImageView
        android:id="@+id/iv_paw_4"
        android:layout_width="65dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="56dp"
        android:layout_marginEnd="104dp"
        android:rotation="-45"
        android:src="@drawable/ic_paw"
        app:layout_constraintEnd_toEndOf="@+id/iv_main"
        app:layout_constraintTop_toTopOf="@id/iv_main"
        tools:ignore="ContentDescription" />

    <!-- Second Background View --> 
    <!-- Guidelines --> 

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

Last updated