ZOFTINO.COM android and web dev tutorials

Android MotionLayout Examples

MotionLayout is a subclass of ConstraintLayout. It allows you to animate property changes of UI elements which are part of a layout. Using MotionLayout, you can animate size, positional changes and other properties of UI elements. Like any other animation, the animation which you implement using MotionLayout should make features of your app more clear to the user.

Table of Contents

Dependencies

To use MotionLayout and ConstraintLayout, you need to add following dependencies to your project.

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'

Uses of MotionLayout

If you want to make user actions on elements visible to user or if you want improve yours app’s look and feel, then you can use MotionLayout to add animations to your app. MotionLayout allows you to animate property changes of UI elements of a layout.

Steps to Use MotionLayout

To use motion layout, first you need to create layout of the screen with the elements to which you want to apply animation using the motion layout. The layout xml should contain MotionLayout element.

Details for animation such as when to trigger the animation, starting layout, ending layout, animation duration and other transition information are defined in the motion scene xml file. So you need to define the motion scene file with starting constraints, ending constrains and transition details.

Then tie the motion scene file to the layout by referencing it in the layout using layoutDescription attribute of MotionLayout element.

In response to the trigger defined for the Transition in motion scene file, the animation gets started from starting state to ending state.

MotionScene

Animation details for animating change of properties of UI elements of a layout from starting state to end state is stored in xml file called motion scene file which is saved in xml folder.

The root element of motion scene xml is MotionScene. The file contains details for animation such as when to trigger the animation, from state, to state, animation duration and other transition details. Tranisition and ConstrainSets elements are defined in the motion scene file.

ConstraintSets are added to the motion scene file to define starting and ending state constraints for the elements involved in the animation.

Transition element contains details such as from state, to state and which element and which action triggers animation.

For example, following motion scene file contains one transition, one constraint set with one constraint which animates button width change on swiping the right side of the button to the right.

<?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:constraintSetStart="@layout/motion_width"
        motion:constraintSetEnd="@+id/new_width"
        motion:duration="2000">
        <OnSwipe motion:touchAnchorId="@+id/button"
            motion:touchAnchorSide="right"
            motion:dragDirection="dragRight"/>
    </Transition>
    <ConstraintSet android:id="@+id/new_width">
        <Constraint
            android:id="@id/button"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:text="wide button"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
    </ConstraintSet>
 </MotionScene>

Transition

Transition element in motion scene file contains animation details. Attributes of transition element are constraintSetStart, constraintSetEnd, interpolator, duration, staggered, onSwipe, onClick and keyFrameSet.

Using constraintSetStart and constraintSetEnd attributes, starting and ending constraints of UI elements involved in the animation are specified, value can be a reference to layout or to constraint set element.

You can specify the rate of animation (accelerate, linear, bounce etc..) using interpolator attribute.

Animation duration can be specified using duration attribute. What triggers the transition can be specified using onSwipe and onClick attributes.

Attribute keyFrameSet allows you to specify modifications to animation.

ConstraintSet & Constraint

You can create animation start and end states of a layout in separate layout files and reference them in the motion scene file. Another way of defining starting and ending states of animation is to define constraint sets in the motion scene file.

ConstraintSet element is used to define set of constraints for start or end states. ConstraintSet constains set of Constraint elements which you can define for each UI element to which you want to apply animation.

Since all the values assigned to the properties of an element are removed when the animation starts, it is important to include in the end state all the properties defined in starting state, including the ones with no value changes.

For a UI element, you need to define constraint both in starting constraint set and ending constraint set and assign different values for the properties which you want to animate.

Triggering MotionLayout Animation

Animation using motion layout can be triggered when an element is clicked or right, left, top or bottom side of the view is swiped to left, right, up or down. The trigger is defined in the motion scene xml file by adding onSwipe or onClick elements transition element as shown in the examples.

Animating Changes of Multiple Properties of an Element

Following motion scene file animates multiple properties such as sclaeX, scaleY and position of an image view.

Layout

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    android:layout_marginTop="32dp"
    app:layoutDescription="@xml/two_properties">
    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleX="0.5"
        android:scaleY="0.5"
        android:src="@drawable/android_i"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

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

Motion scene file

<?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:constraintSetStart="@layout/motion_two_properties"
        motion:constraintSetEnd="@+id/new_image"
        motion:duration="2000">
        <OnSwipe motion:touchAnchorId="@+id/img"
            motion:touchAnchorSide="bottom"
            motion:dragDirection="dragDown"/>
    </Transition>
    <ConstraintSet android:id="@+id/new_image">
        <Constraint
            android:id="@id/img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleX="1.5"
            android:scaleY="1.5"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
    </ConstraintSet>
</MotionScene>

Adding Animation to Multiple Elements

To add animation to multiple elements of a layout, you need to define in the motion scene file the constraint changes for each element that you want to apply animation for.

Following example shows animation on multiple UI elements of a layout. It animates scaleX, elevation properties of progress bar and button.

<?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/prog"
        motion:constraintSetStart="@layout/progress"
        motion:duration="2000">
        <OnClick motion:target="@+id/button" />
    </Transition>
    <ConstraintSet android:id="@+id/prog">
        <Constraint
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progress="20"
            android:scaleX="4"
            android:layout_marginTop="100dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
        <Constraint
            android:id="@+id/button"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="80dp"
            android:text="show"
            android:scaleX="4"
            android:elevation="18dp"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/progressBar" />
    </ConstraintSet>
</MotionScene>

MotionLayout Multiple Transitions Example

To show how to use motion layout with multiple transitions, we will take registration and login screen and implement animation when don’t have account or already have account buttons are clicked to move login and registration buttons.

Layout

Lets create registration screen layout using MotionLayout with required elements for registration screen.

android motionlayout example
<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    android:layout_marginTop="32dp"
    app:layoutDescription="@xml/motion_scene_registration">

    <android.support.design.widget.TextInputLayout
        android:id="@+id/email_l"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <android.support.design.widget.TextInputEditText
            android:id="@+id/email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Email Id" />
    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/password_l"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email_l">

        <android.support.design.widget.TextInputEditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Password"
            android:inputType="textWebPassword" />
    </android.support.design.widget.TextInputLayout>

    <Button
        android:id="@+id/register_b"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Register"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password_l" />

    <Button
        android:id="@+id/reg_login_b"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Already a member? Login."
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/register_b" />

    <Button
        android:id="@+id/login_b"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login"
        app:layout_constraintBottom_toTopOf="@+id/login_reg_b"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

    <Button
        android:id="@+id/login_reg_b"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Don't have account? Register."
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.motion.MotionLayout>

Motion scene file

Motion scene file for our example contains two transition elements. One transition gets triggered when already a member? login button is clicked, which moves registration button down and login button up.

Another transition gets triggered when don’t have account? register button is clicked.

Then add the motion scene file to the layout using layoutDescription attribute of MotionLayout.

<?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/login"
        motion:constraintSetStart="@+id/reg"
        motion:duration="1000">
        <OnClick motion:target="@id/reg_login_b" />
    </Transition>
    <ConstraintSet android:id="@+id/login">
        <Constraint
            android:id="@id/register_b"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Register"
            motion:layout_constraintBottom_toTopOf="@+id/reg_login_b"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
        <Constraint
            android:id="@id/reg_login_b"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Already a member? Login."
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
        <Constraint
            android:id="@id/login_b"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/password_l" />
        <Constraint
            android:id="@id/login_reg_b"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Don't have account? Register."
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/login_b" />
    </ConstraintSet>
    <Transition
        motion:constraintSetEnd="@+id/reg"
        motion:constraintSetStart="@+id/login"
        motion:duration="1000">
        <OnClick motion:target="@+id/login_reg_b" />
    </Transition>
    <ConstraintSet android:id="@+id/reg">
        <Constraint
            android:id="@+id/register_b"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Register"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/password_l" />
        <Constraint
            android:id="@+id/reg_login_b"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Already a member? Login."
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/register_b" />
        <Constraint
            android:id="@+id/login_b"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Login"
            motion:layout_constraintBottom_toTopOf="@+id/login_reg_b"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
        <Constraint
            android:id="@+id/login_reg_b"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Don't have account? Register."
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintLeft_toLeftOf="parent"
            motion:layout_constraintRight_toRightOf="parent" />
    </ConstraintSet>

</MotionScene>

Allowed Attributes for Animation Using MotionLayout

All attributes which can be defined as constraints in constraint layout can be animated using the motion layout and can be added as constraints in the motion scene file. These attributes are mainly positional such as margin attributes (layout_marginTop, etc..) and relative positioning attributes (layout_constraintStart_toStartOf, etc..) and dimensional attributes (layout_width , etc).

In addition to attributes supported by constraint layout, standard attributes such as visibility, alpha, elevation, rotation, scaleY, sclaeX, translationX and translationY can be animated by defining them as constraints in the motion scene file.

MotionLayout Custom Attributes

As shown in the examples above, positional attributes such as margins, layout constraints and dimensions are animated by defining constraints in the motion scene file. To animate attributes such as colors, progress, textsize,..etc, you need to define them as custom attributes.

To define a custom attribute, you need to add CustomAttribute element under constraint element for each custom attribute that you want to animate.

Property name can be specified using attributeName attribute of CustomAttribute element and depending on the type of value the propety takes, you need to use customColorValue, customIntegerValue, customFloatValue, customStringValue, customDimension or customBoolean attributes to assign values.

Following example shows how to use custom attributes with MotionLayout by defining textSize property of TextView and progress property of progress bar as custom attributes.

android motionlayout custom attributes example

Layout

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    android:layout_marginTop="32dp"
    app:layoutDescription="@xml/custom_attributes">
    <TextView
        android:id="@+id/tv1"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:text="hello android"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    <ProgressBar
        android:id="@+id/pb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="10"
        android:max="100"
        android:layout_marginTop="32dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv1" />
    <Button
        android:id="@+id/button"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pb" />

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

Motion scene file

<?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:constraintSetStart="@+id/cust_start"
        motion:constraintSetEnd="@+id/cust_end"
        motion:duration="1000">
        <OnClick motion:target="@+id/button" />
    </Transition>
    <ConstraintSet android:id="@+id/cust_end">
        <Constraint
            android:id="@+id/tv1"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:text="hello android"
            android:layout_marginTop="250dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue ="40"/>
        </Constraint>
        <Constraint
            android:id="@+id/pb"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/tv1">
            <CustomAttribute
                motion:attributeName="progress"
                motion:customIntegerValue ="60"/>
        </Constraint>
        <Constraint
            android:id="@+id/button"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="button"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/pb" />
    </ConstraintSet>
    <ConstraintSet android:id="@+id/cust_start">
        <Constraint
            android:id="@id/tv1"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:text="hello android"
            android:layout_marginTop="16dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue="12"/>
        </Constraint>
        <Constraint
            android:id="@id/pb"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/tv1">
            <CustomAttribute
                motion:attributeName="progress"
                motion:customIntegerValue ="20"/>
        </Constraint>
        <Constraint
            android:id="@id/button"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="button"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/pb" />
    </ConstraintSet>
</MotionScene>

MotionLayout KeyFrameSet

In the previous examples, you have seen how to animate using MotionLayout from a starting state of constraints to ending state of constraints. If you want to include middle states through which animation should go thru, then you will have to use KeyFrameSet.

KeyFrameSet is a child element of Transition and it can contain KeyPosition, KeyAttribute, and KeyCycle elements for defining middle states of animation.

KeyPosition is used to add a position through which the target element passes when it is animated. Using attributes of KeyPosition, position can be specified. Using framePosition attribute of KeyPosition, you can specify the point on the animation path at which middle state is to be added to the animation, it takes any value between 0 to 100, 0 being starting state and 100 being end state.

Deviation from original path of animation can be specified using percentX and percentY attributes to specify deviation along x and y axises at the specified point which is set using framePosition attribute.

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end"
        motion:duration="1000">
        <OnClick motion:target="@+id/wish"/>
        <KeyFrameSet>
            <KeyPosition
            	motion:keyPositionType="pathRelative"
            	motion:percentY="0.30"
            	motion:framePosition="30"
            	motion:target="@id/button"/>
        </KeyFrameSet>
    </Transition>

KeyAttribute is used to add values for attributes for middle state. You can assign values for standard attributes by directly using the name of properties as attribute of KeyAttribute element. For adding non standard attributes, you need to use customAttribute element as child of KeyAttribute. See previous sections for more details on standard and custom attributes.



<KeyAttribute
	motion:framePosition="40"
	motion:target="@id/textview"
	android:rotation="-40">
	<CustomAttribute
    		motion:attributeName="textSize"
    		motion:customFloatValue ="30"/>
</KeyAttribute>

KeyCycle is used to add oscillations during animation. You can specify the point on the animation at which you want oscillation by using framePosition attribute. You can specify the property to which you want to apply oscillation, if it is custom attribute, you can use CustomAttribute element under KeyCycle.

Type of oscillation can be specified using waveShape attribute by assiging any one of the supported oscillations such as sin, square, triangle, sawtooth, reverseSawtooth, cos or bounce. Number of wave cycles can be specified using wavePeriod attribute.


            <KeyCycle
                motion:framePosition="60"
                motion:target="@id/button"
                android:rotation="-60"
                motion:waveShape="bounce"
                motion:wavePeriod="triangle"/>

Following example shows using KeyFrameSet, KeyPosition, KeyAttribute, and KeyCycle. The example animates text view position and size. In the middle of the animation, the text views are assigned new position along y axis using KeyPosition. Rotation and text size properties are assigned new values at that point using KeyAttribute and rotation property is assigned oscillation using KeyCycle.

android motionlayout keyframe example

Motion scene file KeyFrameSet example

<?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:constraintSetStart="@+id/wish_start"
        motion:constraintSetEnd="@+id/wish_end"
        motion:duration="2000">
        <OnClick motion:target="@+id/wish"/>
        <KeyFrameSet>
            <KeyPosition
            motion:keyPositionType="pathRelative"
            motion:percentY="0.25"
            motion:framePosition="50"
            motion:target="@id/tv1"/>
            <KeyPosition
                motion:keyPositionType="pathRelative"
                motion:percentY="-0.25"
                motion:framePosition="50"
                motion:target="@id/tv2"/>
            <KeyAttribute
                motion:framePosition="50"
                motion:target="@id/tv1"
                android:rotation="-30"/>
            <KeyAttribute
                motion:framePosition="50"
                motion:target="@id/tv2"
                android:rotation="-30">
                <CustomAttribute
                    motion:attributeName="textSize"
                    motion:customFloatValue ="20"/>
            </KeyAttribute>
            <KeyCycle
                motion:framePosition="30"
                motion:target="@id/tv1"
                android:rotation="-30"
                motion:waveShape="bounce"
                motion:wavePeriod="3"/>
        </KeyFrameSet>
    </Transition>
    <ConstraintSet android:id="@+id/wish_start">
        <Constraint
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:text="best"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue ="50"/>
        </Constraint>
        <Constraint
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:text="wishes"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/tv1">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue ="50"/>
        </Constraint>
        <Constraint
            android:id="@+id/wish"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="200dp"
            android:text="wish"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>
    <ConstraintSet android:id="@+id/wish_end">
        <Constraint
            android:id="@id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:text="BEST"
            android:layout_marginTop="400dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue ="50"/>
        </Constraint>
        <Constraint
            android:id="@id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:text="WISHES"
            android:layout_marginTop="16dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/tv1">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customFloatValue ="50"/>
        </Constraint>
        <Constraint
            android:id="@id/wish"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="200dp"
            android:text="wish"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>
</MotionScene>

Layout KeyFrameSet example

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    android:layout_marginTop="32dp"
    app:layoutDescription="@xml/keyframeset">
    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:text="Best"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toStartOf="@+id/tv2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:text="Wishes"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/tv1"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/wish"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="wish"
        android:layout_marginTop="200dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

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