MotionLayout(二):MotionLayout是什么?MotionLayout调试技巧、KeyFrame关键帧等等
一、MotionLayout是什么?
● 定位:Android Jetpack 中的高级布局容器,继承自 ConstraintLayout。
● 核心功能:通过状态(State)和过渡(Transition)定义复杂的界面动画,支持手势交互、路径动画等。
● 优势:简化动画开发流程,替代传统 Animator 或 TransitionManager,适合处理多视图联动、复杂转场效果。
1.1 应用场景
- 使用MotionLayout可以实现折叠标题栏,如下图。
- 从一位置到另外一个位置,如下图。
加入这些动画,可以让我们的UI更加有流畅感、科技感。
二、MotionLayout的属性介绍
将ConstraintLayout布局转换为MotionLayout布局后,Android Studio会自动在xml文件夹生成一个对应的MotionScene文件,在该文件中我们可以定义动画的状态、过渡形式、关键帧的属性、和用户的交互方式等。
涉及到的标签、属性如下:
2.1 MotionScene
MotionScene 是 MotionLayout 的核心配置文件。
通过 XML 定义动画的状态(State、ConstraintSet)、过渡规则(Transition)、触发条件(OnClick/OnSwipe)和关键帧(KeyFrame),为 MotionLayout 提供动画的完整逻辑。
1、ConstraintSet:描述视图在不同状态下的布局约束(位置、大小、边距等)。 一般用来定义动画的初始和结束状态。
2、Transition:定义两个状态之间的动画规则(时长、路径、插值器等) ,比如,如何触发(手势触发 ,点击触发等)
3、KeyFrame:插入关键帧(KeyFrame): 通过 KeyAttribute、KeyPosition、KeyCycle 等标签,在动画过程中插入自定义属性变化 ,比如自定义运动路径。
2.2 KeyFrame
KeyFrame 在 MotionLayout 中用于定义动画过渡过程中的关键点,通过在这些关键点上设置特定的属性值或位置,可以实现更复杂和灵活的动画效果。例如,可以让视图在动画过程中偏离默认的直线路径,走一条弧线或自定义曲线,或者在动画过程中实现属性的突变、循环或波动效果 。
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- 动画开始状态 -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/imageView"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- 动画结束状态 -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/imageView"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
<!-- 动画过渡 -->
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start"
app:duration="1500">
<KeyFrameSet>
<!--
在动画进行到50%时,设置imageView的位置
-->
<!--
在动画进行到 50% 时将 imageView 的
Y 方向位置设置为 20%,从而让视图在动画过程中
偏离默认的直线路径,实现弧线运动
-->
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/imageView"
app:percentX="0.5"
app:percentY="0.2"
app:transitionEasing="easeInOut"/>
</KeyFrameSet>
</Transition>
</MotionScene>
2.2.1、KeyPosition
使用 KeyPosition 可以在动画的特定进度点上设置视图的位置,从而让视图偏离默认的直线路径,实现弧线或自定义曲线的运动 .
1.缓动函数控制
app:transitionEasing="easeInOut"
● 动画曲线:采用标准的缓入缓出效果
● 运动效果:
○ 动画启动时逐渐加速(ease-in)
○ 结束前逐渐减速(ease-out)
● 对比其他值:
○ easeIn:仅启动时加速
○ easeOut:仅结束时减速
○ cubic(0.4,0,0.2,1):自定义贝塞尔曲线
2.基础属性解析
app:motionTarget=“@id/imageView”
● 作用对象:指定要控制的视图为 XML 中定义的 imageView
● 注意:标准写法应为 app:target,motionTarget 是旧版兼容写法
app:framePosition="50"
● 时间定位:在动画总时长的 50% 位置插入关键帧
● 取值范围:0-100(对应动画进度 0%-100%)
● 示例:若动画总时长 1000ms,此处对应 500ms 时刻【啥意思】
3.坐标参数详解
app:percentX="0.5"
app:percentY="0.2"
这两个参数的实际含义取决于 app:type 的类型(未显式声明时默认使用 parentRelative)
假设 type=“parentRelative”(默认):
参数 计算方式 当前值示例
percentX 父容器宽度的百分比 (0=左边缘,1=右边缘) 0.5 → 水平居中
percentY 父容器高度的百分比 (0=顶部,1=底部) 0.2 → 距顶部20%高度
其他 type 类型对比:
app:type="pathRelative" 时的效果:
- 沿运动路径的百分比(0=起点,1=终点)
- 示例:percentX=0.5 表示在路径中点位置
app:type="deltaRelative" 时的效果:
- 相对于起点的偏移量(可超过1)
- 示例:percentX=0.5 表示水平移动起点到终点距离的50%
2.2.2、KeyAttribute
用于控制视图的属性变化,例如尺寸、透明度、旋转等。你可以在关键帧中定义这些属性在特定时刻的值。
2.2.3、KeyCycle
KeyCycle 用于控制周期性动画,即动画过程中的重复效果。它允许你创建周期性的动画效果,如摆动或波动。
属性
app:framePosition: 关键帧的位置,范围从 0 到 100,表示动画的百分比。
app:motionTarget: 关键帧作用的目标视图。
app:cycle: 循环次数或周期。
app:waveShape: 波形形状,例如 sine、triangle 等。
app:wavePeriod: 波形周期。
示例
<KeyCycle
app:framePosition="50"
app:motionTarget="@id/targetView"
app:waveShape="sine"
app:wavePeriod="1"
app:rotation="360" />
2.3 Transition
Transition定义动画触发的方式,比如点击,比如拖拽。
<OnClick
app:targetId="@id/button"
app:clickAction="toggle" <!-- 切换 start/end 状态 -->
/>
<OnSwipe
app:touchAnchorId="@id/button"
app:dragDirection="dragRight" <!-- 滑动方向 左右上下-->
/>
还可以支持自动过渡。
方式一:代码中。(推荐)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
val motionLayout = findViewById<MotionLayout>(R.id.motionlayout)
lifecycleScope.launch {
delay(10)
motionLayout.transitionToEnd() // 界面运行就触发动画
}
}
}
方式二:纯 XML 自动触发方式(API 29+)
<Transition
android:id="@+id/transition"
motion:autoTransition="animateToEnd">
...
</Transition>
自动过渡(AutoTransition)
app:autoTransition设置自动过渡的类型。
1. jumpToEnd: 动画直接跳到结束状态。
2. animateToEnd: 动画自动播放到结束状态。
3. jumpToStart: 动画直接跳回开始状态。
4 .animateToStart: 动画自动回到开始状态。
三、调试技巧、注意事项
1、调试技巧:在MotionLayout标签上,加入 app:showPaths="true"可以开启 可视化动画路径。
<?xml version="1.0" encoding="utf-8"?>
<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"
android:background="@drawable/no_nav_bg"
app:showPaths="true"
app:layoutDescription="@xml/backstage_fragment_other_scene">
2、注意事项:constraintSetStart设置的布局,一定要是你原本的布局,constraintSetEnd是结束布局,不然就会出现动画没有平滑移动的效果,变成了一闪而过。
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@layout/backstage_fragment_other_end"
motion:constraintSetStart="@layout/backstage_fragment_other" 开始就是你原本的布局文件
motion:duration="2000">
<OnSwipe
app:dragDirection="dragUp"
app:touchAnchorId="@id/fcv_other"
app:touchAnchorSide="top"
/>
</Transition>
</MotionScene>
参考资料
https://blog.csdn.net/2301_79344902/article/details/141729150
https://blog.csdn.net/m0_74000465/article/details/134236630