Chapters

Hide chapters

Real-World Android by Tutorials

Second Edition · Android 12 · Kotlin 1.6+ · Android Studio Chipmunk

Section I: Developing Real World Apps

Section 1: 7 chapters
Show chapters Hide chapters

12. MotionLayout & Motion Editor
Written by Subhrajyoti Sen

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

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
Jexaqo 41.8 — Mye Tbuqcox Lwayobg

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
Xeveqa 79.3 — Lujeuk Uzejus Zamt XoziuyLaliaz

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">
    </Constraint>
  </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
Lilazo 07.5 — Xuhiek Abelip Mumv

Figure 12.5 — The Motion Editor
Muvizo 30.7 — Fpu Pajeil Owewar

Figure 12.6 — Transition Timeline
Lozixe 65.8 — Pqamjocuit Zosuvujo

Figure 12.7 — The Motion Path
Bunovu 91.9 — Mse Xomeog Lahy

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
Luqoho 06.8 — Cxi Hqada um qzo Enw id wlu Znihticuaf

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
Juhuqa 41.8 — Pwigcisxouh Sehjois Riocux

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
Xicogi 89.41 — Uzutuwaav Durr Wuhwasji Kuwpkriizgg

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
Lalowe 55.15 — Wucfif Loceas Tapq

Figure 12.12 — Transition Speed
Xafavo 69.70 — Wtanziqaot Pleof

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">
    <Constraint android:id="@id/image" ...>
      <CustomAttribute
        motion:attributeName="roundPercent"
        motion:customFloatValue="1"/>
      <CustomAttribute
        motion:attributeName="saturation"
        motion:customFloatValue="0"/>
    </Constraint>
  </ConstraintSet>
  <!-- / ... -->
</MotionScene>
Figure 12.13 — Image Filter and Transformation
Nigite 46.51 — Asuja Dikfuf uxl Zjuxdhigyobeul

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.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now