I first got to know MotionLayout from the Android column of a well-known foreign blog. When I first saw MotionLayout, I was definitely excited. After using and getting familiar with this layout component, I wanted to pass on this joy to domestic developers. From then on, I started to design “fist” and “kick” products 😁. Of course, because the foreign language column about MotionLayout has been introduced in sufficient detail, so this article only summarizes and simple application. As usual, the text begins with a picture:
Introduction to the
Because the audience for this article needs some familiarity with ConstraintLayout, take a few minutes to read my previous translation, which introduces you to the new features of ConstraintLayout 1.1. Back to business, what is a MotionLayout? Many people may be relatively unfamiliar with this noun, but if it comes to its predecessor – ConstraintLayout, we should understand how much. MotionLayout is a new layout component that Google introduced at last year’s Developer conference. Let’s take a look at the official Android definition:
MotionLayout is a layout type that helps you manage motion and widget animation in your app. MotionLayout is a subclass of
ConstraintLayout
and builds upon its rich layout capabilities.
MotionLayout is a layout component that helps you manage gestures and control animations in your app. It is a subclass of ConstraintLayout and builds on its own rich layout capabilities.
Of course, you can also literally refer to it as “motion layout.” Why do you say that? Comparing traditional layout components (FrameLayout, LinearLayout, etc.), we can see that MotionLayout is a “milestone” in layout components. It is a departure from the history of XML files that can only operate “statically” UI. With MotionLayout, we can more easily handle gestures and “motion” effects of its internal child views. As Nicolas Roard puts it:
You can think of it as a combination of property animation, TransitionManager and CoordinatorLayout in terms of MotionLayout functionality.
MotionLayout basis
First, we need to start with some basic properties and usage of MotionLayout, which will help us in the actual operation.
Introducing MotionLayout library
dependencies {
implementation 'com. Android. Support. The constraint, the constraint - layout: 2.0.0 - beta 2'
}
Copy the code
Currently, MotionLayout is still in beta. The animation aid for MotionLayout has not been released yet, but it will be in the next version. It should be possible to partially preview and manipulate parameters directly from the layout editor, as ConstraintLayout does.
Use MotionLayout in the layout file
To use MotionLayout, simply declare the following in the layout file:
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene1">
</android.support.constraint.motion.MotionLayout>
Copy the code
Because MotionLayout is a subclass of ConstraintLayout, it’s natural to use it like ConstraintLayout to constrain subviews, but that’s a bit of an overuse. MotionLayout is much more useful than that. Let’s first look at the composition of MotionLayout:
As you can see from the above figure, MotionLayout can be divided into
and
. The
section is simply interpreted as a ConstraintLayout, and the
is our animation layer. MotionLayout gives us the layoutDescription property, and we need to pass it an XML file wrapped around the MotionScene. To achieve animation interaction, we must connect through this “medium.”
MotionScene: The Legendary “Treasure Bag”
What is a MotionScene? The MotionScene is composed of three parts: StateSet, ConstraintSet and Transition. In order to help you quickly understand and use MotionScene, this article will focus on ConstarintSet and Transition, and StateSet state management will be discussed in a future article. In the meantime, to help you understand, I’ll start with some small concrete examples to help you quickly understand and use it.
Let’s start by implementing the following simple effect:
Sorry for the poor quality of the GIF, but as you can see from the image above, this is a simple pan animation that is triggered by clicking on itself (the basketball). Let’s implement it using MotionLayout. Let’s start with the layout file:
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/step1"
tools:context=".practice.MotionSampleActivity">
<ImageView
android:id="@+id/ball"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_basketball"/>
</android.support.constraint.motion.MotionLayout>
Copy the code
The layout file is simple, except that you may notice that we don’t add any constraints to the ImageView because we declare ConstraintSet in the MotionScene, which will contain constraint information about the start and end of the ImageView’s “movement.” You can also restrict it in the layout file, but control constraints in MotionScene take precedence over Settings in the layout file. Here we use layoutDescription to set the MotionLayout’s MotionScene to Step1. Here we have a look at the MotionScene:
<! --describe the animation for activity_motion_sample_step1.xml-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<! -- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="2200">
<OnClick
app:targetId="@id/ball"
app:clickAction="toggle" />
</Transition>
<! -- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/ball"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
<! -- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/ball"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
</MotionScene>
Copy the code
First, we define two < constraintSets > that describe the start and end of the animation for the 🏀 ImageView (with only the necessary information, such as width, height, margin, and position attributes). Obviously, the ball starts at the top left corner of the screen and ends at the bottom right corner of the screen, so the question is, how do you make it move? And that depends on our
Returning to the previous example, all we need to do is set the start and end constraintsets for Transition, and set the animation time. MotionLayout does the rest automatically. You can also trigger the animation with the onClick event, bind the id of the target control, and set the type of click event through the clickAction property. In this case, we set toggle, which is to switch the state of the animation by clicking the control repeatedly. There are many other attributes can refer to the official documents to study, relatively simple, here will not explain their effect. In this case, run to see the above effect. App :motionDebug=”SHOW_PATH”, and then you can easily see the motion path inside the animation:
What? You said the animation was too basic? Okay, I’m going to do a crude version of “a hundred flowers bloom”, like this:
First, let’s analyze the effect: if we look closely, we can see that by sliding up the blue Android robot, the purple and orange robots fade out and move to the top left and top right corners, respectively. Layout file is very simple, a shuttle OK 😂 :
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:motionDebug="SHOW_PATH"
app:layoutDescription="@xml/step2"
tools:context=".practice.MotionSampleActivity">
<ImageView
android:id="@+id/ic_android_blue"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/android_icon_blue"/>
<ImageView
android:id="@+id/ic_android_left"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/android_icon_purple"/>
<ImageView
android:id="@+id/ic_android_right"
android:layout_width="42dp"
android:layout_height="42dp"
android:src="@mipmap/android_icon_orange"/>
<TextView
android:id="@+id/tipText"
android:text="Swipe the blue android icon up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.motion.MotionLayout>
Copy the code
Let’s look at the MotionScene in step2:
<! --describe the animation for activity_motion_sample_step2.xml-->
<! --animate by dragging target view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<! --At the start, all three stars are centered at the bottom of the screen.-->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/ic_android_blue"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginBottom="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/ic_android_left"
android:layout_width="42dp"
android:layout_height="42dp"
android:alpha="0.0"
android:layout_marginBottom="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/ic_android_right"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginBottom="20dp"
android:alpha="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
<! --Define the end constraint to set use a chain to position all three stars together below @id/tipText.-->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/ic_android_left"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginEnd="90dp"
android:alpha="1.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/ic_android_blue"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
<Constraint
android:id="@+id/ic_android_blue"
android:layout_width="58dp"
android:layout_height="58dp"
app:layout_constraintEnd_toStartOf="@id/ic_android_right"
app:layout_constraintStart_toEndOf="@id/ic_android_left"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
<Constraint
android:id="@+id/ic_android_right"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginStart="90dp"
android:alpha="1.0"
app:layout_constraintStart_toEndOf="@id/ic_android_blue"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tipText"/>
</ConstraintSet>
<! -- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end">
<! -- MotionLayout will track swipes relative to this view -->
<OnSwipe app:touchAnchorId="@id/ic_android_blue"/>
</Transition>
</MotionScene>
Copy the code
The code above is easy to understand. We defined a Constraint for the control earlier. Now we just need to add two more constraints. ConstraintLayout because the three Android robots have the same starting position and only the blue one is displayed, simply set the transparency of the other two robots to 0 at the beginning position and separate the three robots at the end position. Then set the opacity of the left and right Android robots at the end position to 1. Once the animation starts, MotionLayout automatically handles the alpha property of the target control so that it still looks smooth.
In addition, we do not use
to trigger the animation. Similarly, we use
to trigger the animation. We only need to specify touchAnchorId as a small blue robot. In addition, you can specify the desired sliding gesture and sliding direction by specifying touchAnchorSide, dragDirection, etc. The default is sliding up. We will use the sliding gesture interchangeably in later examples. You can check the official documentation if you can’t help it. OK, so, we above the pseudo “a hundred flowers bloom” the effect has been realized, it doesn’t matter difficult right 😄.
At this point, you might say: The first two examples always have a “straight line” animation trajectory. What if you want a “curve” animation trajectory? B: Sure! Keyframes help you arrange!
KeyFrameSet: Make animation unique
KeyFrameSet is a powerful property if we want to achieve “unique” animation interactions. It can change the position and state information of a key frame during our animation. This may not be easy to understand, but let’s take a look at this example:
It is not difficult to find with your eyes that the movement track of the windmill is a curve, and when it is rotated and magnified to the middle position, it will reach the zero bound point and then begin to shrink. The layout code is pretty simple, but the only thing that matters is the MotionScene effect we need to implement — step3.xml:
<! --describe the animation for activity_motion_sample_step3.xml-->
<! --animate in the path way on a view-->
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<! -- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/windmill"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@id/tipText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/windmill"
app:layout_constraintTop_toTopOf="@id/windmill"/>
</ConstraintSet>
<! -- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<! --this view end point should be at bottom of parent-->
<Constraint
android:id="@id/windmill"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="12dp"
android:layout_marginEnd="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Constraint
android:id="@+id/tipText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:alpha="1.0"
android:layout_marginEnd="72dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</ConstraintSet>
<! -- A transition describes an animation via start and end state -->
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end">
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/windmill"
app:keyPositionType="parentRelative"
app:percentY="0.5"/>
<! --apply other animation attributes-->
<! -- The first half of the animation effect: counterclockwise rotation, at the same time double zoom -->
<KeyAttribute
app:motionTarget="@id/windmill"
android:rotation="360"
android:scaleX="2.0"
android:scaleY="2.0"
app:framePosition="50"/>
<! -- Animation effect of the second half: counterclockwise rotation, while changing back to the original -->
<KeyAttribute
app:motionTarget="@id/windmill"
android:rotation="720"
app:framePosition="100"/>
<! -- Delay animation -- Keep opacity at 0.0 during 0-85 -->
<KeyAttribute
app:motionTarget="@id/tipText"
app:framePosition="85"
android:alpha="0.0"/>
</KeyFrameSet>
<OnSwipe
app:touchAnchorId="@id/windmill"
app:touchAnchorSide="bottom"
app:dragDirection="dragRight"/>
</Transition>
</MotionScene>
Copy the code
From the above code we can see: KeyFrameSet needs to be included in Transition, and KeyFrameSet defines two elements,
and
, that are used to set the key frame at one point in the animation. To specify the desired effect for an animation. As the name implies, KeyPosition is used to specify the position of a keyframe in an animation, and KeyAttribute is used to describe the configuration of attributes (such as transparency, scaling, rotation, etc.) for a keyframe in an animation. In addition, KeyFrameSet also supports
and
to make animations more interesting and flexible, which will be covered in a future article due to space constraints.
Let’s look at the composition of KeyPosition:
As can be seen from the figure above, there are three keypositiontypes. ParentRelative is used in this paper, that is, the layout of the entire MotionLayout is the coordinate system, and the upper left corner is the origin of the coordinate system, that is, referring to the coordinate system of View. The other two types will be explained and applied uniformly in subsequent articles. The difference is just the reference points that the coordinate system selects. We use the framePosition attribute to specify the position of the key frame. The value ranges from 0 to 100. In this example, 50 is the midpoint of the animation. In addition, you can set the offset of the keyframe position by specifying percentX and percentY, which are generally 0 — 1, or negative or greater than one. For example, if the offset is not set, the animation will be a straight line parallel to the X axis. But by setting app:percentY=”0.5″, the windmill will be moved half the height in the animation toward the Y axis, as shown below (start debug mode) :
One might ask: Why is the trajectory not a triangle, but a curve? Haha, that’s a good question! Because MotionLayout automatically makes the keyframe position as smooth as possible, it makes animation execution less rigid. Other code should be easier to understand, you can refer to the documentation to understand.
Now that we know how to use KeyFrameSet, we can easily implement the following effect:
MotionLayout code will be uploaded to GitHub, interested friends can go to see. Unconsciously already said so much, but found that there are a lot of content is not involved or speak clearly, due to the space is limited, can only be put in the following several periods to introduce for you 😄. If you have any questions or suggestions for this article, please leave them in the comments section.
The entire code of this article:Github.com/Moosphan/Co…
More advanced usage will be followed in future articles, and the warehouse will be updated
Reference and thanks:
Introduction to MotionLayout (part I)
Introduction to MotionLayout (part II)
Introduction to MotionLayout (part III)
Defining motion paths in MotionLayout
MotionLayout development
The last
The starting point of this article is to hope that only for everyone to provide a “keyhole” role, through the “hole”, we can vaguely see the door “treasure” after light, want to open the door to find treasure, we need to “every effort”, get the “key” to open the door 😄. Of course, stay tuned for more MotionLayout in my upcoming games.