The preparatory work

It takes three steps to put 🐘 in the fridge, and two to get MotionLayout on your project

Step 1: Upgrade ConstraintLayout to 2.0 and above

 implementation 'androidx. Constraintlayout: constraintlayout: 2.0.0 rc1'
Copy the code

At the time of writing, the latest version is 2.0.0-rc1 (2.0.0 is a bit long to develop, the stable version is still 1.1.3).

Step 2: Convert the layout to MotionLayout

ConstraintLayout is a subclass of ConstraintLayout. You can write a layout using ConstraintLayout. You can also use MotionLayout.

ConstraintLayout: Convert it to MotionLayout

Open the layout in the View preview or select ConstraintLayout in the Component Tree and right click on the Covert to MotionLayout option in the menu bar





Get familiar with the MotionLayout workbench

After we convert ConstraintLayout to MotionLayout, we create a MotionScene file in res/ XML /



The file content is as follows


      
<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">
       <KeyFrameSet>
       </KeyFrameSet>
    </Transition>

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

    <ConstraintSet android:id="@+id/end">
    </ConstraintSet>
</MotionScene>
Copy the code

This file is associated with the layout by declaring app:layoutDescription in the layout file

<androidx.constraintlayout.motion.widget.MotionLayout...app:layoutDescription="@xml/activity_main_scene"
   >
Copy the code

Looking at the layout,Android Studio (4.0 and above) brought me a visual animation editing tool called MotionLayout, as shown below



**① ** Normal state. After selected, the preview view displays the original state

**② ** indicates ConstraintSet of id=”start”. Select this ConstraintSet and the preview view displays the layout of this ConstraintSet

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

💡 ConstraintSet specifies the position and properties of all views at a certain point in the animation sequence. In general, a Transition element can refer to two ConstraintSet elements, one of which defines the beginning of the animation sequence and the other defines the end of the animation sequence

**③ ** represents the ConstraintSet of id=”end”. When selected, the preview view displays the layout of the ConstraintSet. **④ ** represents the transition from the start ConstraintSet to end

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

**⑤ ** Create a new ConstraintSet **⑥ ** Create a new Transition Transition **⑦ ** Create an action that triggers a Transition, whether it is Click or swipe

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <! - click - >
        <OnClick />
        <! Slide -- - >
        <OnSwipe />
    </Transition>
Copy the code

Look closely at these three ICONS

**⑧ ** there is a tutorial to help



**⑨ ** represents an element of the constraint set

The beginning and end of the animation

Animation has a beginning and an end. We enjoy the process from start to finish.

Get started with a simple effect

preview

Implement a simple rectangle pan animation

💡 The above figure is the running effect, the dotted line in the figure indicates the motion track. To show the dotted line of the motion path you need to set the showPaths property of the MotionLayout to true app:showPaths=”true”

Creating animated elements

Create a View in the layout as follows

<androidx.constraintlayout.motion.widget.MotionLayout...app:layoutDescription="@xml/activity_main_scene"
    app:showPaths="true">

     <View
        android:id="@+id/box"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:background="@color/colorAccent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>
  
</androidx.constraintlayout.motion.widget.MotionLayout>
Copy the code

Define the animation start & end state (ConstraintSet)

Add ConstraintSet (id=”box”) to ConstraintSet (id=”start”

This will create a new constraint for View(id=”box”) in the constraint set (id=”start”). The XML code for this wave of operations is as follows

 <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintStart_toStartOf="parent" />
    </ConstraintSet>
Copy the code

ConstraintSet is a collection of view constraints and properties. ConstraintSet is described by the Constraint tag. Constraint(id=”box”) Constraint(id=”box”)

  <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
Copy the code

ConstraintSet(id=”end”); ConstraintSet(id=”end”); ConstraintSet(id=”end”);

<ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>
Copy the code

Transitions (Transition)

View(id=”box”)start and end constraints have been written

   <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <! - click - >
        <OnClick />
    </Transition>
Copy the code

Transition definition visualization is the line with the arrow in the red area in the image above. Notice that there is also a little dot on the line that means clicking

The Transition label, through motion: constraintSetStart specifies the movement state (its value is ConstrainSet id) to motion: constraintSetEnd specified movement end state transitions, Motion :duration to specify how long the animation will be executed. Transition just defines the specified start and end states for the user to trigger ta, and adds or under the Transition tag

💡 Transition has a property motion :autoTransition that sets the behavior of automatically performing transitions without the user having to do so.

OnClick

It is triggered by a user click

attribute

  1. motion:targetId(id of target View)

If you don’t specify a sub-property, you click the whole screen and if you write this property, you click the View with the corresponding ID to trigger the transition

  1. motion:clickActionThis property can be set to the following values

TransitionToStart Transition to < Transition > element motion: : constraintSetStart attribute specified by the state, Has a transitionToEnd Transition to a state specified by the
element motion:constraintSetEnd property, Have excessive animation jumpToStart jump straight to the < Transition > element motion: : constraintSetStart attribute specified by the state, No animation effect jumpToEnd jumps directly to the state specified by the
element’s Motion :constraintSetEnd property. This is the default toggle value. In < the Transition > element motion: : constraintSetStart and motion: constraintSetEnd to specify the layout of the switch between, if in the start state to end state, If you’re in the end state you’re in the start state, you’re in the end animation.

OnSwipe

Represents triggered by the user’s slide, which adjusts the progress of the animation based on the user’s slide behavior. A Transition tag can contain multiple OnSwipe. The sample

   <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <OnSwipe
            motion:dragDirection="dragEnd"
            motion:touchRegionId="@+id/box" />
    </Transition>
Copy the code

It has the following properties motion:touchAnchorId Which has the value of the View Id, and slides to make the corresponding View

💡 OnSwipe listens for MotionLayout, not Motion :touchAnchorId

Slide to the side of the target view to which you are fixed. MotionLayout will try to maintain a constant distance between the fixed point and the user’s finger. Acceptable values include “left”, “right”, “top”, and “bottom”. Motion :dragDirection Direction of the user’s sliding action. If this property is set, this

will only apply to slides in a particular direction. Acceptable values include “dragLeft”, “dragRight”, “dragUp”, and “dragDown”.

This is an interesting property, because you can make the target follow the direction of your finger, you can go in the opposite direction of your finger, or you can go in the vertical direction of your finger, depending on how you set this property, right

Motion :dragScale controls how far the view moves relative to the sliding length. The default value is 1, indicating that the view should move the same distance as the slide distance. If the dragScale is less than 1, the view will move much less than the slide distance (for example, a dragScale of 0.5 means that if the slide moves 4 centimeters, the target view will move 2 centimeters). If the dragScale is greater than 1, the view will move farther than the slide distance (for example, a dragScale of 1.5 means that if the slide moves 4 cm, the target view will move 6 cm). Motion :maxVelocity The maximum speed of the target view. When the finger slides a certain speed, the target View will continue to run in inertia, accelerating and then slowing down (default). If the motion accelerates to the maximum we set (according to our junior high school physics knowledge, can reach the maximum velocity, depending on the acceleration (aaa), time (TTT), and the initial velocity (v0v_0v0), v=v0+ ATV =v_0+ ATV =v0+ AT), then it is accelerated first. It’s moving at its maximum speed, and then it’s slowing down. Motion :maxAcceleration Specifies the maximum acceleration of the target View. If you want the View to move faster, you need to increase the acceleration

Property transition animation

ConstraintSet specifies constraints and properties. In the translation example above, we only used constraints. Let’s see what happens when we add attributes.

1. Set the transparency attribute

</ConstraintSet>

<ConstraintSet android:id="@+id/end">
      <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:alpha="1"
            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/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:alpha="0.1"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>
</ConstraintSet>
Copy the code

Set at the start position of the animationandroid:alpha="1"End Position SettingAndroid: alpha = "0.1"MotionLayout will automatically overdo it for us, and it will look like this



2. Set a rotation attribute

  <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:rotationX="0"... The constraints are as above />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/box"
            android:layout_width="64dp"
            android:layout_height="64dp"... The constraints are as above />
    </ConstraintSet>
Copy the code

Set at the start position of the animationandroid:rotationX="0" Android :rotationX=”0″

You can also change other properties to try, such as setting different background color, setting different size, etc

Animation of the movement process 🏃

The animation above is an animation where we define the start and end states and then it transitions itself. So let’s talk about if we interfere in the course of his movement

Key frames (KeyFrameSet)

KeyFrameSet is a child of Transition

By default, we only need to define the start and end states of the animation, and MotionLayout will smooth the transition. If I want to do a more complicated motion, I need the KeyFrameSet to set it up, so we set certain constraints or properties at certain points in the motion of the animation, the transitions between the points, the MotionLayout will also help us smooth the transitions.

KeyFrameSet can contain KeyPosition, KeyAttribute, KeyCycle, KeyTimeCycle, and KeyTrigger, which can be added by editing, as shown in the following figure



If we click select (1), there will be a timeline tool. If we click play (2), we can preview the animation in the editor. If we click eat (3), we can see the option to add children to the KeyFrameSet

KeyPosition

You can change the position of the animation as it moves.

So the movements we’ve done up here are straight lines, and now I’m going to do curve movements with KeyPosition





The diamond points in the figure are the ones we modified with KeyPosition, as shown below

  <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <OnClick motion:targetId="@id/box"/>
        <KeyFrameSet>
            <KeyPosition
                motion:framePosition="50"
                motion:motionTarget="@id/box"
                motion:keyPositionType="parentRelative"
                motion:percentY="0.3"
                motion:percentX="0.8"/>
        </KeyFrameSet>
    </Transition>
Copy the code

Added several attributes to KeyPosition above

motion:framePosition Indicates the progress of the animation. The value range is [0,100]. The 50 above indicates 50% of the animation progress.

motion:motionTargetIndicates the view ID of the path to be modified

motion:keyPositionTypeRepresents the coordinate system type. The value can be parentRelative, pathRelative or deltaRelative

motion:percentYmotion:percentXIt’s the ratio of the vertical to the horizontal relative to the frame of reference.



Let’s focus on keyPositionType

1.parentRelative

Motion :percentX=”0.8″; motion:percentY=”0.3″; motion:percentY=”0.3″







I have drawn two figures above, as we said above (0.3,0.8) should be the two grey focal points of Figure 1, but we found that Figure 1 is a little bit different from the actual point. When we specify a point with KeyPosition, we specify the center point of the view. Figure 1 is ideal, and if our view is infinitesimally small, that’s what happens in Figure 1. In fact, our view cannot be infinitesimally small, and the real situation should look like figure 2.

2.deltaRelative

In this type, the starting point coordinate of the view is (0,0) and the end point coordinate is (1,1), as shown in the diagram

  <KeyFrameSet>
        <KeyPosition
            motion:framePosition="50"
            motion:motionTarget="@id/box"
            motion:keyPositionType="deltaRelative"
            motion:percentY="0.5"
            motion:percentX="0.8"
            />
    </KeyFrameSet>
Copy the code


This time deliberately move the end point of the view to the upper right corner, otherwise the starting position and the end position will be on the same level as defined above, and the X and Y axes will coincide in deltaRelative.

3.pathRelative

This is interesting, the starting point (0,0) is still the starting point of the view, and the ending point of the view is (1,0), so the frame is set up as follows

The positive Y axis is 90 degrees clockwise on the X axis, which is exactly the same as the View coordinate system in Android, which is the opposite of what we’re learning.

KeyAttribute

KeyAttribute allows us to change the attribute at a certain point in the animation process.

 <Transition
    motion:constraintSetEnd="@+id/end"
    motion:constraintSetStart="@id/start"
    motion:duration="2000">
    <OnClick motion:targetId="@id/box"/>
        <KeyFrameSet>
            <KeyAttribute
               motion:framePosition="50"
                motion:motionTarget="@id/box"
                android:rotationY="180"
                android:alpha="0"
                />
        </KeyFrameSet>
</Transition>
Copy the code

We added a KeyAttribute to the KeyFrameSet and specified the attribute Android :rotationY=”180″, which works like this



You can see that the view rotates from 0 to 180 along Y, and then from 180 to 0 again, and the transparent animation proceeds from 1 to 0, and then from 0 to 1.

You can also try to change the actual property values and try it out.

KeyAttibute defines a View native to Android, Android: Visibility, Android :alpha, Android: Elevation, Android: Rotation, Android :rotationX, Android :rotationY, Android :scaleX, Android :scaleY, Android :translationX, Android :translationY, Android :translationZ You can use the CustomAttribute, a child of KeyAttibute

<KeyFrameSet>
            <KeyAttribute
                android:rotationY="180"
                motion:framePosition="50"
                motion:motionTarget="@id/box">
                <CustomAttribute
                    motion:attributeName="rectangleColor"
                    motion:customColorValue="@color/colorAccent" />
            </KeyAttribute>
        </KeyFrameSet>
Copy the code

CustomAttribute specifies two attributes. AttributeName specifies the name of the CustomAttribute. The value of the attribute can be color, integer, float, string, size, or Boolean. So you use the Motion :customColorValue property to specify its value.

KeyCycle

You can have the view change its property values periodically during the animation process according to some periodic functions



The above example is to change the Android :translationY property periodically during the animation [0%,80%] with the period function sin

 <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="2000">
        <OnClick />
        <KeyFrameSet>
            <KeyCycle
                motion:framePosition="80"
                motion:motionTarget="@+id/box"
                motion:wavePeriod="1"
                motion:waveShape="sin"
                android:translationY="50dp"/>
        </KeyFrameSet>
    </Transition>
Copy the code

Motion :framePosition=”80″ means KeyCycle extends to 80% of the animation, motion:wavePeriod means number of cycles in motion, motion:waveShape means type of cycle, this is specified as sin, It’s going to be a sine periodic function.

KeyTimeCycle

According to some periodic functions on the key frame, its attribute value is changed periodically, and the effect is shown in the following figure



So you can see KeyTimeCycle compared to KeyCycle, KeyTimeCycle cycles on frames, KeyCycle cycles during the animation

<Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="2000">
        <OnClick />
        <KeyFrameSet>
            <KeyTimeCycle
                motion:motionTarget="@+id/box"
                motion:wavePeriod="1"
                android:translationY="50dp"
                />
        </KeyFrameSet>
    </Transition>
Copy the code

The parameters are similar to KeyCycle. If we specify the number of periods of motion:wavePeriod, the change frequency will be faster.

KeyTrigger

In this class, we define two public functions who() and where(), as follows (the simplest custom View 😄).

class MyTextView(context: Context? , attrs: AttributeSet?) : AppCompatTextView(context, attrs) {fun who(a) {
        text = Who am I?
    }

    fun where(a) {
        text = "There I am."}}Copy the code

The layout code is as follows

<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"... > <View android:id="@+id/box"... /> <com.wkk.motionlayoutdemo.MyTextView android:id="@+id/myTextView"... /> </androidx.constraintlayout.motion.widget.MotionLayout>Copy the code

MotionScene core code

 <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="2000">
        <OnClick />
        <KeyFrameSet>
            <KeyTrigger
                motion:framePosition="20"
                motion:motionTarget="@id/myTextView"
                motion:onCross="who" />
            <KeyTrigger
                motion:framePosition="80"
                motion:motionTarget="@id/myTextView"
                motion:onCross="where" />
        </KeyFrameSet>
    </Transition>
Copy the code

FramePosition The progress of the animation, motionTarget the target of action, Motion :onCross the name of the function to fire

OnCross is an animation, whether it’s forward or backward, and it executes as soon as it reaches the set framePosition. Two other functions are also fired. OnPositiveCross will only execute if the forward animation reaches the set framePosition. The opposite is true of onNegativeCros.

The above definition is to trigger the view whose ID is myTextView at 20% of the animation progresswhoMethod to trigger the myTextView view when the animation progress is 80%whereMethod, which works as follows.



Write in the last

MotionLayout can help us achieve a lot of animation effects, it has been out for a long time, it should be very likely to release a stable version, it is time to learn a wave, the above is my study of MotionLayout record some knowledge points, there are a lot of knowledge points of MotionLayout, The above is just a simple demonstration of some properties, but more practice is needed to achieve complex animations. MotionLayout has a very good editor, at the beginning of learning XML is not too good to write, you can use graphical editing tools to click, see what it helps us to generate XML is like, I also pasted some reference links for learning, MotionLayout together to learn.

Advanced article

MotionLayout Instruction manual (advanced + Actual combat)

Refer to the link

MotionLayout official documentation MotionLayout/ConstraintLayout official sample (making) using MotionLayout for Android applications to add animation MotionLayout (Codelab) API