Home Android & Kotlin Books Real-World Android by Tutorials

12
MotionLayout & Motion Editor Written by Subhrajyoti Sen

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Animations and transitions are a great way of improving your app’s user experience. Android has a wide set of classes you can use to implement different kinds of animations, but historically, using them to create anything complex has been difficult. Fortunately, Android introduced MotionLayout in ConstraintLayout 2.0 to address these problems.

MotionLayout makes it possible to implement detailed animations entirely in XML, similar to the way you create layouts. In this chapter, you’ll learn how to use MotionLayout to

  • Animate view dimensions.
  • Translate views.
  • Preview the animation in the IDE.
  • Change the shape of images and apply filters.

Getting to know MotionLayout

Before you start creating beautiful animations with MotionLayout, you need to learn about its three main concepts:

  1. MotionScene: This is the root element for every animation scene. It contains the different states of the animation and the transitions between them.
  2. ConstraintSet: A collection of Constraint tags. A Constraint is a set of ConstraintLayout attributes that you apply to a specific view. Typically, you’ll have two ConstraintSets that define the start and end states of the animation. Although you can have more ConstraintSets in theory, XML only lets you use two. If you need to use more than two states in the animation, you have to do that programmatically.
  3. Transition: Defines the transition between two ConstraintSets. You can also set properties, like the animation duration and the interpolator, to change the values of the constraints.

This is in the context of ConstraintLayout, where you represent the state of a specific View, or a group of Views, as the set of the constraints you apply to them. Different constraints produce a different state for the Views. You then use a Transition to represent how you go from one state to another.

Finally, as Figure 12.1 shows, a MotionScene is a way to aggregate different states for a View and the way you transition from one to another. This produces an animation.

Figure 12.1 — The Concept Behind MotionScene
Figure 12.1 — The Concept Behind MotionScene

Getting started

Open the starter project from the downloaded materials and run it, then go to the details page of any pet. You’ll notice that the layout of the page is a bit different from what you implemented in the previous chapter. This change adds scrollable content, which lets you create gesture-based animations.

Figure 12.2 — The Starter Project
Hazexo 26.0 — Llu Dbezhev Rpavesp

Defining a MotionLayout

You can define a MotionLayout declaratively by using an XML document. Create a file named fragment_details_scene.xml in res/xml and insert the following code:

<MotionScene
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">

  <ConstraintSet android:id="@+id/start"> 
  </ConstraintSet>

  <ConstraintSet android:id="@+id/end"> 
  </ConstraintSet>

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

</MotionScene>
<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:motionInterpolator="cubic(.17,.67,.83,.67)" 
    motion:duration="1000">
  </Transition>  

</MotionScene>

Applying MotionScene to a View

Now, you need to apply the MotionScene you just created to a specific View. To do this, first open fragment_details.xml and add the following:

<androidx.constraintlayout.motion.widget.MotionLayout
  android:id="@+id/motion_layout" 
  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"
  app:layoutDescription="@xml/fragment_details_scene"> <!-- HERE -->
  <!-- // ... -->
</androidx.constraintlayout.motion.widget.MotionLayout>  
Figure 12.3 — Layout Editor With MotionLayout
Jajisu 83.9 — Puloup Ivarac Rejl NowiazQalooz

Adding your first constraint

As mentioned above, MotionLayout works by transitioning between two states, where each state is represented by a ConstraintSet. Inside each ConstraintSet, you have multiple Constraints corresponding to different views. You only need to define a Constraint for the views you want to animate, not every view.

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">
  <!-- / ...-->
  <ConstraintSet android:id="@+id/end">
    <Constraint
      android:id="@id/image"
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:layout_marginBottom="@dimen/default_margin"
      android:layout_marginStart="@dimen/default_margin"
      android:layout_marginTop="@dimen/default_margin"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toTopOf="parent" />
  </ConstraintSet>
  <!-- / ...-->
</MotionScene>

Motion Editor

Motion Editor is a handy tool that comes built-in with Android Studio 4.0 and later. It lets you preview animations created with MotionLayout without having to leave the IDE. Additionally, it provides a Graphical User Interface to add and edit different ConstraintSets, Constraints, Transitions and much more.

Figure 12.4 — Layout Editor Tabs
Quyeze 07.6 — Totiey Afawaz Zadl

Figure 12.5 — The Motion Editor
Ruriwa 41.9 — Hjo Qimueg Esaras

Figure 12.6 — Transition Timeline
Juzaja 05.9 — Rcovrupeer Jozowobi

Figure 12.7 — The Motion Path
Jeceda 82.0 — Tfo Fegeum Qery

Adding a trigger

Animations seldom start on their own; they’re usually associated with an event or user interaction. For example, you click on a button to load something and the button animates to a progress bar. It would be weird for this animation to start on its own.

Adding OnSwipe

Open fragment_details_scene.xml and add the <OnSwipe/> element to Transition:

<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"
    motion:motionInterpolator="linear">
    <OnSwipe
      motion:dragDirection="dragUp"
      motion:touchAnchorId="@id/scrollView" />
  </Transition>

</MotionScene>
Figure 12.8 — The Scene at the End of the Transition
Pitawa 02.9 — Vro Hzuqa eb jhi Oth iy bju Smamzadaep

Overriding visibility

MotionLayout controls the visibility of all its child views. Even if you tried to control the visibility of child views programmatically, it wouldn’t have any effect. Luckily, MotionLayout provides functionality to ignore this behavior.

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">

  <ConstraintSet android:id="@+id/start">
    <Constraint android:id="@+id/loader">
      <PropertySet motion:visibilityMode="ignore" />
    </Constraint>

    <Constraint android:id="@+id/call">
      <PropertySet motion:visibilityMode="ignore" />
    </Constraint>

    <Constraint android:id="@+id/scrollView">
      <PropertySet motion:visibilityMode="ignore" />
    </Constraint>
  </ConstraintSet>
  <!-- // ... -->
</MotionScene>

Figure 12.9 — Transaction Without Loader
Ladigo 86.7 — Ztahlagcuaj Yogciug Luejur

Animating more features

The current transition animates only the pet’s image. How about animating the pet’s name and the Call button as well? In this section, you’ll add constraints to:

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">
  <!-- // ... -->
  <ConstraintSet android:id="@+id/end">
    <!-- // ... -->
    <Constraint
      android:id="@+id/call"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="@dimen/default_margin"
      android:rotation="180"
      motion:layout_constraintBottom_toBottomOf="parent"
      motion:layout_constraintStart_toEndOf="parent" />

    <Constraint
      android:id="@+id/name"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="@dimen/default_margin"
      android:layout_marginStart="@dimen/default_margin"
      android:layout_marginTop="@dimen/default_margin"
      android:scaleX="1.4"
      android:scaleY="1.4"
      motion:layout_constraintStart_toStartOf="parent"
      motion:layout_constraintTop_toBottomOf="@+id/image" />
  </ConstraintSet>
  <!-- // ... -->
</MotionScene>
Figure 12.10 — Animation With Multiple Constraints
Hiweze 14.42 — Osapeseeq Viww Purgadho Zevngbaujmg

Adding non-linear motion

In the current version of the animation, the pet’s name takes a linear path during the transition, as the dashed line you saw in the Motion Editor preview shows. The path line is straight, denoting linear animation. However, the transition would look much better with a curved path.

<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"
    motion:motionInterpolator="linear">
    <KeyFrameSet>
      <KeyPosition
        motion:framePosition="50"
        motion:keyPositionType="parentRelative"
        motion:motionTarget="@id/name"
        motion:percentX="0.4" />
    </KeyFrameSet>
    <!-- // ... -->
  </Transition>
</MotionScene>
Figure 12.11 — Curved Motion Path
Fepeza 81.98 — Veghar Humeos Bavl

Figure 12.12 — Transition Speed
Xotabe 36.49 — Myuyyetuek Bkuik

ImageFilterView

In addition to MotionLayout, ConstraintLayout 2.0 also introduced a utility class named ImageFilterView, which extends AppCompatImageView and makes it easy to apply filters to images. Now, you no longer need to include a new third-party library to get a circular ImageView. With ImageFilterView, you get out-of-the-box support to change the radius of the image, crossfade between two images, change the image saturation and, much more.

CustomAttribute

Look closely at all the view attributes you’ve animated so far and you’ll notice that these are attributes that apply to any View or affect the positions of the different views. It’s not possible to assign a custom property in any Constraint.

<androidx.constraintlayout.motion.widget.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/motion_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layoutDescription="@xml/fragment_details_scene">
  <!-- // ... -->
  <androidx.constraintlayout.utils.widget.ImageFilterView
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:contentDescription="@string/image_of_pet"
    android:scaleType="centerCrop"
    tools:src="@drawable/cute_doggo"
    app:layout_constraintDimensionRatio="H,1:1"
    app:layout_constraintTop_toTopOf="parent"
    app:roundPercent="0" />
  <!-- // ... -->
</androidx.constraintlayout.motion.widget.MotionLayout>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">

  <ConstraintSet android:id="@+id/start">
    <!-- / ... -->
    <Constraint
      android:id="@+id/image"
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:contentDescription="@string/image_of_pet"
      motion:layout_constraintTop_toTopOf="parent"
      motion:layout_constraintDimensionRatio="H,1:1">
      <CustomAttribute
        motion:attributeName="roundPercent"
        motion:customFloatValue="0"/>
      <CustomAttribute
        motion:attributeName="saturation"
        motion:customFloatValue="1"/>
    </Constraint>
  </ConstraintSet>
  <!-- / ... -->
</MotionScene>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:motion="http://schemas.android.com/apk/res-auto">
  <!-- / ... -->
  <ConstraintSet android:id="@+id/end">
    <!-- / ... -->
    <CustomAttribute
      motion:attributeName="roundPercent"
      motion:customFloatValue="1"/>
    <CustomAttribute
      motion:attributeName="saturation"
      motion:customFloatValue="0"/>
  </ConstraintSet>
  <!-- / ... -->
</MotionScene>
Figure 12.13 — Image Filter and Transformation
Vabico 70.75 — Ewosa Vagvif ibf Syihygebxujier

Key points

  • MotionLayout is an extension of ConstraintLayout that lets you write complex animations in a declarative way through XML.
  • You can use Motion Editor to preview animations without leaving your IDE.
  • A Transition defines the start and end state of the motion as well as properties like the motion’s duration.
  • A ConstraintSet defines a state in the transition. It consists of a collection of Constraints for each view that you’ll animate.
  • A KeyFrameSet specifies attributes and locations of views at distinct points in the transition.
  • CustomAttribute sets attribute values that are either View properties or are unrelated to the position.
  • Use ImageFilterView to apply common filters to images and also change properties like the radius.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.