This article has been published exclusively by guolin_blog, an official wechat account
Android MotionLayout is a guide to the latest Android animation layout!
The environment
- system : macOS
- Android studio: 4.1.3
- ConstraintLayout: 2.0.4
- Gradle: gradle – 6.7.1 – bin
- Kotlin: 1.4.23
What do you need to know
- MotionLayout is a subclass of ConstraintLayout and has all the functions of ConstraintLayout
Implementation ‘androidx. Constraintlayout: constraintlayout: 2.0.4’
- Preview MotionLayout with AS. The AS version must be >= 4.0
Create an animation using MotionLayout
Convert ConstraintLayout to MotionLayout
After conversion:
So you can see that we’ve defined a Button, and if we say ConstraintLayout, we’ll warn you to constrain it, but we don’t
This is because the MotionLayout property is defined in the layoutDescription property
Click animation [OnClick]
OnClick# clickAction description:
type | instructions | rendering |
---|---|---|
motion:clickAction=”toggle“ | reverse | |
motion:clickAction=”transitionToEnd“ | Animate to the second | |
motion:clickAction=”jumpToEnd“ | End without adding animation | |
motion:clickAction=”jumpToStart“ | Do not add animation to start | The name implies… |
motion:clickAction=”transitionToStart“ | Add an animation to | The name implies… |
Multiple Views are linked together
For example:
Effect:
Finger drag [OnSwipe]
Parameter Description:
- Motion :touchAnchorId refers to a view that you can slide and drag.
- Motion :touchAnchorSide means we drag the view from the right.
- Motion :dragDirection Indicates the drag progress direction. For example, motion:dragDirection=”dragRight” means that progress increases as you drag to the right.
Effect:
Auxiliary tool
MotionLayout comes with debugging tools by default.
Description table:
state | instructions | rendering |
---|---|---|
app:motionDebug=”SHOW_ALL“ | Auxiliary lines and progress display | |
app:motionDebug=”SHOW_PATH“ | Auxiliary line display | |
app:motionDebug=”SHOW_PROGRESS“ | According to schedule |
Official illustration:
- The circle represents the starting or ending position of a view.
- Lines represent the path of a view.
- The diamond represents the path where KeyPosition is modified.
There is another way to debug using views:
Modifying path (KeyPosition)
The path can be modified using the visualization tool on the right:
, a graph:
Explanation 1 | Steps 2 |
---|---|
Let’s look at the auto-generated code:
KeyPosition parameter description:
- Motion :motionTarget=”@+id/button” The view that needs to move the track
- Motion :framePosition=”[0-100]” framePosition is a number between 0 and 100. It defines the time to be applied in the animation KeyPosition, with 1 representing 1% of the animation and 99 representing 99% of the animation
- motion:keyPositionType=” ”KeyPositionType This is how KeyPosition modifies the path. It can be
parentRelative
.pathRelative
Or,deltaRelative
- PercentX | percentY is to modify the path framePosition (value between 0.0 and 1.0, allows the negative value and value > 1)
Motion :keyPositionType=” ”
type | instructions | Coordinate system | rendering |
---|---|---|---|
pathRelative | It’s completely different from the other two because the X-axis follows the motion path from beginning to end. So, (0,0) is the starting position, and (1,0) is the ending position. | ||
deltaRelative | The term “deltaRelative” is also used to describe relative change. In deltaRelative coordinates, (0,0) is the starting position of the view and (1,1) is the ending position. The X and Y axes are aligned with the screen. The X-axis is always horizontal on the screen, the Y-axis is always vertical on the screen. The main difference from parentRelative is that coordinates describe only the part of the screen within which the view will move. | The effect is similar to That of pathRelative, no demo | |
parentRelative | ParentRelative uses the same coordinate system as the screen. It defines (0, 0) in the upper left corner of the entire MotionLayout and (1, 1) in the lower right corner. |
ParentRelative: Four points are set here, respectively:
Assuming that we now move the starting point 1,deltaRelative builds a ‘Bezier environment’ from the starting point and the starting point 2 to generate the corresponding path, which is similar to the effect of pathRelative but different!
The difference between pathRelative and deltaRelative:
- PathRelative does not depend on the start and end points to drag
- DeltaRelative starting and ending points on the same X-axis will cause animation paths not to be executed on bezier paths. I’m going to execute a line.
As shown in the figure:
type | instructions | rendering |
---|---|---|
deltaRelative | Depending on where you start and where you end | |
PathRelative or parentRelative | You don’t have to rely on starting and ending points |
The end result looks like this:
The effect is the same, just put one, need to download the source code to watch…
KeyPosition is advanced with pathMotionArc
PathMotionArc is used to draw arcs.
Let’s start with a simple example of pathMotionArc:
As you can see, drawing an elegant arc is as simple as setting motion at the start point :pathMotionArc=”startHorizontal”
⚠️: Motion :pathMotionArc requires 2 points, the default is the start point and the end point
How do you set multiple points? For example:
You can also set the scale of the two arcs
Location coordinate description:
PathMotionArc:
- StartVertical Indicates a downward arc
- StartHorizontal upward arc
- None linear
- If the previous point was startVertical, then the current point is startHorizontal, which can be understood as inversion
type | instructions | rendering |
---|---|---|
Effect of a | The starting point: motion:pathMotionArc=”startVertical“ The starting point 1: motion:pathMotionArc=”startHorizontal“ The starting point 2: motion:pathMotionArc=”startHorizontal“ |
|
Effects of two | The starting point: motion:pathMotionArc=”startVertical“ The starting point 1: motion:pathMotionArc=”none“ The starting point 2: motion:pathMotionArc=”startHorizontal“ |
|
The effect of three | The starting point: motion:pathMotionArc=”startVertical“ The starting point 1: motion:pathMotionArc=”startVertical“ The starting point 2: motion:pathMotionArc=”flip“ |
The code for effect 1, effect 2, and Effect 3 is highly repetitive, so here’s just the code for Effect 3:
Effect three code:
<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">
<! -- Click event -->
<OnClick
motion:clickAction="toggle"
motion:targetId="@id/button6" />
<KeyFrameSet>
<! -- Start point 1 and start point 2 set "association" -->
<KeyPosition
motion:framePosition="40"
motion:keyPositionType="deltaRelative"
motion:motionTarget="@+id/button6"
motion:pathMotionArc="startVertical"
motion:percentX="0.358"
motion:percentY="0.17" />
<! -- Start point 2 and end relation -->
<KeyPosition
motion:framePosition="79"
motion:keyPositionType="deltaRelative"
motion:motionTarget="@+id/button6"
motion:pathMotionArc="flip"
motion:percentX="0.675"
motion:percentY="0.568" />
</KeyFrameSet>
</Transition>
<! -- start -->
<ConstraintSet android:id="@+id/start">
<! Start point and start point 1 set "association" -->
<Constraint
android:id="@+id/button6"
android:layout_width="80dp"
android:layout_height="80dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:pathMotionArc="startVertical" />
</ConstraintSet>
<! -- end -->
<! -- The end point does not set the association. -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/button6"
android:layout_width="64dp"
android:layout_height="64dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintRight_toRightOf="parent" />
</ConstraintSet>
</MotionScene>
Copy the code
Here the ratio can also be changed, for example:
Changing the state of an attribute (KeyAttribute)
Take a look at the code:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/ It's over"
motion:constraintSetStart="@id/ Here we go"
motion:duration="1000">
<KeyFrameSet>
<! -- Change attribute state -->
<KeyAttribute
motion:motionTarget="@+id/button7"
motion:framePosition="22"
android:alpha="0.2" />
</KeyFrameSet>
<OnClick
motion:clickAction="toggle"
motion:targetId="@id/button7" />
</Transition>
<! It is also possible to define Chinese at the beginning, but it is not recommended. -->
<ConstraintSet android:id="@+id/ Here we go">
<Constraint
android:id="@+id/button7"
android:layout_width="60dp"
android:layout_height="60dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<! Chinese can also be defined here, but it is not recommended. -->
<ConstraintSet android:id="@+id/ It's over">
<Constraint
android:id="@+id/button7"
android:layout_width="60dp"
android:layout_height="60dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
Copy the code
Change the alpha value:
You can also set more than one property, for example:
Explore by yourself:
- android:visibility
- android:alpha
- android:elevation
- android:rotation
- android:rotationX
- android:rotationY
- android:scaleX
- android:scaleY
- android:translationX
- android:translationY
- android:translationZ
KeyAttribute works with CustomAttribute to set the color
- CustomAttribute Is a KeyAttribute attribute that is used to set the view color
Let’s look at the code:
<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">
<! - click - >
<OnClick
motion:clickAction="toggle"
motion:targetId="@id/imageView8" />
<KeyFrameSet>
<! -- Turn 3 times to 100 points -->
<KeyAttribute
android:rotation="720"
motion:framePosition="100"
motion:motionTarget="@id/imageView8" />
<! -- black at 0 o 'clock -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/imageView8">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="# 000000" />
</KeyAttribute>
<! -- Red at 50 o 'clock -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/imageView8">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#E91E63" />
</KeyAttribute>
<! -- Black at 100 -->
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/imageView8">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="# 000000" />
</KeyAttribute>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start" . />
<ConstraintSet android:id="@+id/end" . />
</MotionScene>
Copy the code
Inside the CustomAttribute you must specify an attributeName and a value to set.
- Motion :attributeName is the name of the setter that this custom attribute will call. In this example, setColorFilteronDrawable will be called.
- motion:customColorValueIs a custom value for the type indicated in the name, or in this case, the specified color. Custom values can have any of the following types:
- Color
- Integer
- Float
- String
- Dimension
- Boolean
Take a look at the renderings:
Set jitter [KeyCycle]
How to create:
Key code:
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<! - click - >
<OnClick . />
<KeyFrameSet>
<! -- waveOffset added to the property's offset motion:wavePeriod Number of loops to loop around this region Motion :wavePeriod ="cos" Sin | square | triangle | sawtooth | reverseSawtooth | cos | | bounce sine square reverse sawtooth | | | | serrated triangle cosine | rebound -- -- >
<KeyCycle
android:alpha="0.5"
android:scaleY="1.2"
android:scaleX="1.2"
motion:framePosition="51"
motion:motionTarget="@+id/imageView8"
motion:waveOffset="2"
motion:wavePeriod="1"
motion:waveShape="sin" />
</KeyFrameSet>
</Transition>
Copy the code
Parameter Description:
- Motion :waveOffset added to the offset value of the property
- Motion :wavePeriod Number of loops to loop around this region
- motion:waveShape=”cos”
- sin|square|triangle|sawtooth|reverseSawtooth|cos|bounce
Effect pictures of various types:
sin | square | triangle | sawtooth | reverseSawtooth | cos | bounce |
---|---|---|---|---|---|---|
sine | square | triangle | The sawtooth | Reverse the sawtooth | cos | rebound |
Set Jitter (KeyTimeCycle)
KeyTimeCycle works the same as KeyCycle, and the parameters are the same.
A little different is that KeyTimeCycle is usually used together with three KeyTimeCycle cycles to define an exact keyframe
It will only change at frame 50 when the Motion :wavePeriod is set to 1
- Motion :wavePeriod: Number of loops to loop around this region
Changing control properties (KeyTrigger)
What does it mean to change control properties? How to control?
First, the effect:
Let’s start by customizing ImageView with two methods: show and hide
class KeyTriggerImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) :
AppCompatImageView(context, attrs, defStyleAttr) {
/ / display the view
fun show(a) {
visibility = View.VISIBLE
}
// Hide the current view
fun hide(a) {
visibility = View.GONE
}
}
Copy the code
How to use:
<? 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/end"
motion:constraintSetStart="@id/start"
motion:duration="2000"> <! <OnClick Motion :clickAction="toggle"
motion:targetId="@id/imageView11" />
<KeyFrameSet>
<KeyTrigger
motion:framePosition="0"
motion:motionTarget="@+id/imageView11"
motion:onCross="show" />
<KeyTrigger
motion:framePosition="20"
motion:motionTarget="@+id/imageView11"
motion:onCross="hide" />
<KeyTrigger
motion:framePosition="60"
motion:motionTarget="@+id/imageView11"
motion:onCross="show" />
<KeyTrigger
motion:framePosition="79"
motion:motionTarget="@+id/imageView11"
motion:onCross="hide" />
<KeyTrigger
motion:framePosition="100"
motion:motionTarget="@+id/imageView11"
motion:onCross="show" />
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start". /> <ConstraintSet android:id="@+id/end". /> </MotionScene>Copy the code
KeyTrigger parameters:
- Motion: the name of the method called by onCross
- What frame is currently in (0-100)
- Motion: Id of the control set by motionTarget
Take a look at the results:
According to this idea, it is possible to replace the icon display in the process of sliding, for example:
One more auxiliary image, it should be very clear now!!
Acceleration and deceleration (Easing)
Also a parameter effect: very simple to use, directly look at the code:
<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">
<! - click - >
<OnClick
motion:clickAction="toggle"
motion:targetId="@id/imageView13" />
<KeyFrameSet>
</KeyFrameSet>
</Transition>
<! - began to -- -- >
<ConstraintSet android:id="@+id/start">
<! -- Motion :transitionEasing Sets the acceleration or deceleration type -->
<Constraint
android:id="@+id/imageView13"
android:layout_width="100dp"
android:layout_height="100dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintLeft_toLeftOf="parent"
motion:transitionEasing="acclerate" />
</ConstraintSet>
<! End -- -- -- >
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/imageView13"
android:layout_width="100dp"
android:layout_height="100dp"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
Copy the code
Motion :transitionEasing
type | The auxiliary figure | The effect |
---|---|---|
standard | ||
acclerate | ||
decelerate |
Common on these kinds, there are many others, not a demonstration.
The effect changes are very subtle, their own manual make a understand!!
In actual combat
Let’s take a look at the actual results:
The layout looks like this:
Demand analysis:
- Similar to mantis shrimp click comment function
- When you click the comment button, the image shrinks and a RecyclerView pops up underneath to display the comment
- Comment button not displayed when RecyclerView is displayed
- Recyclerview is not displayed when the comment button is displayed
activity_motion_layout_9_scene.xml:
<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="400">
<OnClick
motion:clickAction="toggle"
motion:targetId="@id/imageComment" />
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@id/recyclerView" />
</Transition>
<! - began to -- -- >
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/imageComment"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginRight="@dimen/dp_10"
android:alpha="1"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintVertical_bias="0.7" />
<Constraint
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
motion:layout_constraintTop_toBottomOf="@id/image" />
</ConstraintSet>
<! End -- -- -- >
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
motion:layout_constraintBottom_toTopOf="@id/recyclerView"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="500dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintTop_toBottomOf="@id/image" />
<Constraint
android:id="@+id/imageComment"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginRight="@dimen/dp_10"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintVertical_bias="0.7" />
</ConstraintSet>
</MotionScene>
Copy the code
This is basically the end of it
conclusion
General structure
<Transition
app:constraintSetStart="@+id/start"
app:constraintSetEnd="@+id/end"
app:duration="1000">
<! - drag - >
<OnSwipe />
<! - click - >
<OnClick />
<KeyFrameSet >
<KeyAttribute>
<CustomAttribute/>
</KeyAttribute>
<KeyPostion/>
<KeyCycle/>
<KeyTimeCycle/>
</KeyFrameSet>
<! -- Start point state parameter configuration for transition animation -->
<Constraint android:id="@id/viewId">
<! -- Motion model: arc path, time model, etc.
<Motion/>
<! < span style = "box-sizing: border-box; color: RGB (51, 51, 51); display: block; line-height: 22px; font-size: 14px! Important; word-break: normal; word-break: normal;
<Layout/>
<! -- Animation transform: Do rotation, displacement, scaling, sea altitude and other properties -->
<Transform/>
<! -- Custom attributeName adds set/get reflection to find the real function name, SetBackgroundColor () custom(XXX)Value specifies the data type of the property -->
<CustomAttribute/>
<! -- Specific properties visibility, alpha, etc. -->
<PropertySet/>
</Constraint>
<! -- End point state parameter configuration for transition animation -->
<ConstraintSet android:id="@+id/end"./>
</Transition>
Copy the code
The complete code
Other:
-
Git Clone github.com/googlecodel…
-
The official documentation
-
Reference document 1
-
Reference 2
Original is not easy, your praise is the biggest support for me!