Git address
Github.com/ananananzhu…
Welcome to pay attention to the public number: Ananzhuo to learn more knowledge
Simple implementation effect
Simulator recording, relatively slow, suggested patience to finish watching
Use an overview
MotionLayout is a child of layout, ConstraintLayout. Specifically used to achieve motion control animation in the process,
For example, we can set up the layout by MotionLayout in the layout file layoutA. In the layout, we set up two child controls TextView and ImageView. For example, if we want the ImageView to slide up when the finger touches up in the layout, we need to create an XML layout file called scenceA under the XML folder and handle the slide in scenceA. We create two ConstraintSet/Constraint nodes in the scenceA XML file. One node is start, which represents the state of the layout before the animation starts, and the other node represents the state of the layout after the animation ends. When the animation completes, the ImageView transitions from the start state to the end state.
Take the end state as an example:
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/iv_0"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:rotation="720"
app:layout_constraintTop_toTopOf="parent" >
<CustomAttribute
app:customDimension="100dp"
app:attributeName="end"
/>
</Constraint>
</ConstraintSet>
Copy the code
Common apis
You can take part in the official documentation by reading my introduction to the explanation and more detailed documentation:
https://developer.android.google.cn/training/constraint-layout/motionlayout/ref?hl=zh_cn
MotionScence
As the root node in the animation layout
ConstraintSet
You can store Constraint nodes. Typically, an animation XML file can have two ConstraintSet nodes, A, which is the state before the animation starts, and B, which is the state at the end of the animation.
For example, the width height of the control in node A is 100dp, and the width height of the animation in node B is 300DP. When the animation starts to execute, it will be an enlarged animation. The width and height of the control will be enlarged from 100dp to 300DP in the form of animation.
Code examples:
<ConstraintSet android:id="@+id/start">/ / the node A<Constraint
android:id="@+id/iv_0"
android:layout_width="10dp"
android:layout_height="10dp"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">/ / the node B<Constraint
android:id="@id/iv_0"
android:layout_width="100dp"
android:layout_height="100dp"/>
</ConstraintSet>
Copy the code
Properties in
Constraint
Each Constraint can animate a control, as shown in the following code:
We will set the width and height and layout constraint properties for the control with control ID IV_0 in our layout.
The Constraint attribute
The Constraint property is exactly the same as the ConstraintLayout property,
<Constraint
android:id="@+id/iv_0"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" >
Copy the code
The Constraint attribute
The Constraint property is exactly the same as the ConstraintLayout property,
The Constraint attribute
The Constraint property is exactly the same as the ConstraintLayout property,
Transition, onClick, onSwipe
Transition can be used with multiple onClicks and onSwpie
Transition All properties
The Transition, onSwipe
OnSwipe is used to control animations in slides, such as in the following code,
DragDirection indicates the method by which the control is dragged
TouchAnchorId: indicates that the id of the touched control is IV_0
TouchAnchorSide: indicates that the direction of touch is from left to right
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start"
app:duration="2000">
<OnSwipe
app:dragDirection="dragRight"
app:touchAnchorId="@id/iv_0"
app:touchAnchorSide="right" />
</Transition>
Copy the code
OnSwipe all attributes
The Transition, the onClick
OnClick is similar to onSwipe in that the animation starts when we click the control.
I won’t repeat the point
KeyFrameSet, KeyPosition, and KeyAttribute
KeyFrameSet must be inside the Transition node
KeyFrameSet can be paired with KeyPosition and KeyAttribute for more precise animation control
For example, we can configure multiple KeyPositions in KeyFrameSet to control the state of motion corresponding to a certain percentage of animation execution
The code controls here really can’t be described well in words, so write some code and show some effects
The scence code
Layout file code:
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:showPaths="true"
app:layoutDescription="@xml/anim_3_scene">
<ImageView
android:id="@+id/iv_03"
android:layout_width="50dp"
android:src="@drawable/apple"
android:layout_height="50dp"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Copy the code
Scence code:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetEnd="@id/end"
app:duration="500"
app:constraintSetStart="@+id/start">
<OnSwipe
app:dragDirection="dragRight"
app:touchAnchorId="@id/iv_03"
app:touchAnchorSide="right" />
<KeyFrameSet>
<KeyPosition
app:framePosition="20"
app:motionTarget="@id/iv_03"
app:keyPositionType="pathRelative"
app:percentY="0.4"
/>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/iv_03"
app:keyPositionType="pathRelative"
app:percentY="0.2"
/>
<KeyPosition
app:framePosition="80"
app:motionTarget="@id/iv_03"
app:percentY="0.4"
app:keyPositionType="pathRelative"
/>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/iv_03"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/iv_03"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
Copy the code
Implementation effect
The animation will follow the following rules
More useful use of a single attribute
OnSwipe.touchRegionId
TouchRegionId can be set to touch the touched control. For example, in this case, we set app:touchRegionId=” @ID /iv” so that the animation only happens when our finger touches the ImaveView. Otherwise, if we don’t set touchRegionId, we can swipe globally.
The drawback of touchRegionId is that when we set touchRegionId to a MotionLayout, none of its child views can execute a hit event. (The Down event is directly intercepted from the onInterceptTouchEvent.) No processing space). This situation can be solved by customizable MotionLayout, which I have not tried so far. I will deal with it when I use it in the project.
Example:
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@+id/start">
<OnSwipe
app:dragDirection="dragLeft"
app:touchRegionId="@id/iv"
app:touchAnchorId="@id/iv"
app:touchAnchorSide="left" />
</Transition>
Copy the code
Layout file
Scence file
OnSwipe.touchAnchorSide
Slide the side of the target view to which it is fixed. MotionLayout will attempt to maintain a constant distance between this fixed point and the user’s finger. Acceptable values include “left”, “right”, “top”, and “bottom”.
Don’t be too dramatic, for example, if you swipe to the right, but touchAnchorSide is set to top, it will still slide horizontally to the right of the view
OnSwip.dragDirection
The direction in which the user slides has four optional values: “dragLeft”, “dragRight”, “dragUp” and “dragDown”.
The OnClick attribute
It’s easier to understand, so I just took a screenshot of the official website
KeyPosition
Most of the content is copy official website
motion:motionTarget
The view from which its motion is controlled.
motion:framePosition
An integer between 1 and 99 that specifies when the view in the motion sequence reaches this specified point. For example, if framePosition is 25, the view reaches the specified point one quarter of the way along the entire motion path.
Motion: percentX, motion: percentY
Specify where the view should go. The keyPositionType property specifies how to interpret these values.
motion:keyPositionType
Specifies how to interpret the percentX and percentY values. Possible Settings include:
parentRelative
PercentX and percentY are specified relative to the superview. X is the horizontal axis, ranging from 0 (left end) to 1 (right end). Y is the vertical axis, where 0 is the top and 1 is the bottom.
For example, if you want the target view to reach some point in the middle of the right end of the parent view, set percentX to 1 and percentY to 0.5.
deltaRelative
PercentX and percentY are specified relative to how far the view moves throughout the motion sequence. X is the horizontal axis, Y is the vertical axis; In both cases, 0 is the starting position of the view on that axis, and 1 is the final position.
For example, suppose the target view moves 100 dp up and then 100 dp to the right, but you want the view to move as follows: first move 40 DP up in the first quarter of the motion, then move up in an arc. To do this, set framePosition to 25, keyPositionType to deltaRelative, and percentY to -0.4.
pathRelative
The X-axis is the direction in which the target view moves within the path range, where 0 is the starting position and 1 is the final position. The Y-axis is perpendicular to the X-axis, with positive values on the left side of the path and negative values on the right; Setting a nonzero percentY makes the view arc in one direction or the other. Therefore, the initial position of the view is (0,0) and the final position is (1,0).
For example, suppose you want the view to move 10 percent of the distance in the first half of the motion sequence, and then accelerate to cover the remaining 90 percent. To do this, set framePosition to 50, keyPositionType to pathRelative, and percentX to 0.1.
Use MotionLayout to create nested slides
The sample code
Code sample
The design
The results of this example are as follows:
Implementation approach
The green View is a MotionLayout viewA and the red View is a NestScrollView viewB. ViewA and viewB exist in the same MotionLayout viewP.
The first scence activity_nest_scroll_scene is mounted in viewP, which handles the implementation of viewA scaling up and viewB sliding up as we slide viewB.
Our implementation also needed to change the green text from vertical to horizontal as we swiped up. This requires that we also add a scence to the MotionLayout in the viewA node, which handles the display of text changes
Key points to implement code
- If you want
header
Internally nestedscence
Follow externalscence
If linkage is required, set this parameter
app:motionProgress="1"
The motionProgress property is the key,
We need to set the header motion progress to 1 in the end state of the external scence animation, so that the internal Scence will keep the same animation progress while the external scence animation is in progress
The code in the example is shown below:
Implementation effect
Implementation code (skip)
- Outermost layout viewP code
App :layoutDescription=”@xml/activity_nest_scroll_scene” used to set the animation between header and NestScrollView
File name: activity_nest_scroll.xml
<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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NestScrollActivity"
app:layoutDescription="@xml/activity_nest_scroll_scene">
<include layout="@layout/motion_header"/>
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollable"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/large_text" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.motion.widget.MotionLayout>
Copy the code
- ViewP scence code (activity_nest_scroll_scene)
File name: activity_nest_scroll_scene.xml
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:motion="http://schemas.android.com/apk/res-auto"
>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="250"
motion:motionInterpolator="linear">
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@+id/motionLayout"
motion:touchAnchorSide="bottom" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/scrollable"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/motionLayout" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
app:motionProgress="1"
app:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/scrollable"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/motionLayout" />
</ConstraintSet>
</MotionScene>
Copy the code
- ViewA code
This part of the code was added to the layout in Part 1 using the include tag
File name: motion_header.xml
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1e376b"
app:layoutDescription="@xml/scene_17_header"
app:showPaths="true">
<TextView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#0a3"
android:scaleType="centerCrop"
/>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Safe and sound."
android:textColor="#FFF"
android:textSize="32dp"
android:transformPivotX="0dp"
android:transformPivotY="0dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="200dp" />
</androidx.constraintlayout.motion.widget.MotionLayout>
Copy the code
- ViewA scence code
File name: scene_17_header.xml
<! Copyright (C) 2018 The Android Open Source Project Licensed under The Apache License, Version 2.0 (The "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -->
<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/background"
motion:touchAnchorSide="bottom" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/background"
android:layout_width="match_parent"
android:layout_height="200dp"
android:alpha="1.0"
android:scaleX="1.1"
android:scaleY="1.1"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.6"
android:rotation="90.0"
android:translationY="8dp"
motion:layout_constraintBottom_toBottomOf="@+id/guideline"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
motion:layout_constraintGuide_begin="200dp" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/background"
android:layout_width="match_parent"
android:layout_height="200dp"
android:alpha="0"
android:translationX="0dp"
android:translationY="0dp"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:rotation="0.0"
motion:layout_constraintBottom_toBottomOf="@+id/guideline"
motion:layout_constraintStart_toStartOf="parent" />
<Constraint
android:id="@id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
motion:layout_constraintGuide_begin="56dp" />
</ConstraintSet>
</MotionScene>
Copy the code