MotionLayout
MotionLayout的介绍:
-
是什么?
MotionLayout
是一种布局类型,能够帮我们实现复杂的动画效果,还能与人进行交互;简单来说,我们可以在布局中定义多个状态,
Motionlayout
帮助我们在这些状态中平滑的过渡,从而实现复杂的动画效果; -
MotionLayout
是ConstraintLayout
的子类,所以具有ConstraintLayout
类同样强大的功能,所以我们只需要添加ConstraintLayout
的依赖,就可以使用MotionLayout
了; -
我们可以通过XML文件中声明性的设置,就可以完成动画效果了,当然我们也可以通过Java代码设置,拿到
Motionlayout
的实例,使用一些方法进行设置,后文会提到; -
MotionLayout
只能为它直接子级设置动画;
实现
添加依赖
java
implementation "androidx.constraintlayout:constraintlayout:2.2.0"
也可以去官网查看最新版
使用Motionlayout
方式一:
xml布局中component Tree
> 点击第一个子项 > 点击Convert to MotionLayout
;
这种方法系统会自动把ConstraintLayout
替换成MotionLayout
,并且生成MotionScene
;MotionScene
可以在你的res/xml目录下找到类似于nihao\_scene.xml
为名的文件;

方式二:
手动替换为MotionLayout
,以及创建xml目录下MotionScene
为根布局的文件;
MotionLayout
文件如下:
java
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutDescription="@xml/motionsence"
android:background="@drawable/second">
<ImageView
android:id="@+id/page"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/first">
</ImageView>
</androidx.constraintlayout.motion.widget.MotionLayout>
布局很简单,MotionLayout
布局里面写了一个view;
MotionScene(运动性描述文件)
-
是什么?
相应布局的所有运动描述;也就是说我们定义什么样的动画,就在这个文件里设置;
-
每个
MotionLayout
都引用一个单独的MotionScene
;那么怎么将二者联系起来呢?
app:layoutDescription="@xml/motionsence"
:通过MotionLayout
的 app:layoutDescription
联系起来;
-
MotionScene
中的定义的运动描述优先于MotionLayout
中的任何相同定义;比如定义宽高,最终显示出来的效果是MotionScene
中定义的宽高;MotionLayout
其他属性:showPaths
:布尔类型,表示在运动进行时是否显示运动路径。默认值为 false;currentState
:可指定具体的ConstraintSet
,告诉MotionLayout
从哪个状态开始滑动;
其实重要的还是怎么使用动画达到我们想要的效果,所以我们来详细看看MotionScene
;
一个运动性描述文件框架上主要有Transition
,ConstraintSet
,Constraint
,OnClick,
OnSwipe
;
简单分析完成一段动画需要什么,我们得确定动画的刚开始的状态,结束时的状态,怎么触发这个动画,一个布局里面有很多个view,如何正确的把刚开始的状态以及结束状态应用到理想的view上;那么我们围绕以上内容进行学习;
ConstraintSet
- 介绍:
<ConstraintSet>
元素是用来指定视图在动画序列中某一点上的位置和属性(可同时指定多个视图),也叫约束条件集合;
通俗来讲:这就是决定动画某一时刻(比如起始)的状态,我们可以在这里设置很多元素的起始状态,所以这个就要做约束条件集合;
- 代码模式:
java
<ConstraintSet android:id = "@+id/end">
<Constraint
...
</Constraint>
<Constraint
...
</Constraint>
</ConstraintSet>
-
属性:
android:id = "@+id/end"
指定这个约束集的名字;deriveConstraintsFrom
:值为另一个ConstraintSet
的 ID;如果指定这个属性,ID 对应约束集合内的所有约束条件都将应用于此集合,除非此集明确替换它们;
-
里面必须包含一个或者多个
Constraint
元素;
Constraint
- 介绍:
<Constraint>
元素用来声明运动序列其中一个视图的位置和属性,也就是视图的约束;
通俗来讲:规定具体的view的位置以及属性,为它设置状态;
- 代码:
java
<Constraint
android:id = "@+id/TODO"
android:layout_height="64dp"
android:layout_width="64dp"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp">
<CustomAttribute
motion:attributeName="background"
motion:customColorValue="#CCA4E3">
</CustomAttribute>
</Constraint>
-
属性:
android:layout_height="64dp"
这里设置宽高的优先级高于view自身;
这里有一个疑问?为什么在主布局中定义了view的宽高,为什么这里还要再定义一遍?
答案:主布局设置的是初始值,而这里设置的是在这个动画中这个状态下的view的大小;
2.android:id = "@+id/TODO"
这里的ID不是为了起名字,而是为了指定作用于哪个view;
CustomAttribute
-
介绍:对view变化过程中进行插值,是运行时属性,比如背景颜色,文字颜色,圆角半径等;
-
代码:
java<CustomAttribute motion:attributeName="backgroundColor" motion:customColorValue="#D8C7D5"> </CustomAttribute>
-
属性:
motion:attributeName
你要变化的属性名motion:customColorValue
变化的数值
常见的属性表如下:
属性类型 对应的字段名 示例 说明 颜色 customColorValue #FF4081 可对颜色进行渐变(自动补间 RGB) 浮点数 customFloatValue 0.5 常用于透明度、圆角半径、进度等 整数 customIntegerValue 100 如宽高、边距、进度值等 布尔 customBooleanValue true 通常配合状态切换使用 字符串 customStringValue "Hello" 很少用于动画,但可以保存状态 维度 customDimension 16dp 常用于阴影半径、圆角、边距等 -
如果你要定义,那么这个view的全程都应该写
CustomAttribute
;
Transition
-
介绍: 该元素是用来声明运动过程中的开始和结束状态,包括所有预期的过度状态、用户的触发的交互等。
通俗来讲:我们可能会定义很多个状态,那么它就是将开始的状态和结束的状态连接到一起作为一个动画,就是
Transition
的作用 -
代码:
java
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/middle"
>
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
-
属性:
motion:constraintSetStart="@+id/start"
确实运动过程中的起始状态;motion:constraintSetEnd="@+id/middle"
同理结束状态;motion:duration="500"
动画持续的时间,如果没写,默认父元素中设置的时间,所以我们父元素也就是MotionScene中可以设置,属性是motion:defaultDuration="1000"
-
Transition
可以包含的元素:OnClick
,OnSwipe
,KeyFrameSet
;
OnClick
- 介绍: 该元素用于指定当用户点按特定视图时要执行的操作,指定当用户点击视图时触发动画序列;
通俗来讲:就是点击触发动画;
- 代码
java
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
-
属性:
motion:targetId="@+id/TODO"
你要滑动触发的view的对象;motion:clickAction="transitionToEnd"
代表点击触发的操作;
clickAction属性值附表
属性值 含义 行为描述 典型使用场景 transitionToEnd 切换到 end 状态 从当前状态(通常是 start)平滑过渡到 end 点击按钮触发开启动画,例如菜单展开、视图滑出 transitionToStart 切换到 start 状态 从当前状态(通常是 end)回到start 点击关闭按钮,恢复初始位置 toggle 自动在 start / end 之间切换 如果当前在start 就去 end,反之返回 start 单个按钮控制展开 / 收起 jumpToEnd 立即跳转到 end 状态 不进行动画过渡,直接到结束布局 测试时或某些需要立即切换的场景 jumpToStart 立即跳转到 start 状态 不进行动画过渡,直接回到起始布局 重置布局 none 无操作 点击无任何动画或状态变化 占位、禁用点击行为
其实jumpToStart
和transitionToStart
是区别就是有无动画,下面是一个手动start
和end
之间切换的实例:
java
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
</Transition>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/middle"
motion:duration="500">
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToStart">
</OnClick>
</Transition>
我们可以看到两个transition
定义的起始和结束状态都是一样的,那么设立了两个点击触发,就可以达到点击到结束状态,再点击回到初始状态;
- 为什么不写在一个
Transition
里面?只能写一个,写多了编译器报错;
实现的效果如下:

OnSwipe
- 介绍: 该元素用于指定当用户在不居中滑动时需要执行的操作;
通俗来讲:就是滑动触发动画;
- 代码
java
<OnSwipe
motion:touchAnchorId="@id/page"
motion:touchRegionId="@id/page"
motion:dragDirection="dragLeft"
motion:onTouchUp="autoComplete"
>
</OnSwipe>
-
属性:
motion:touchAnchorId="@id/page"
你的目标view的ID;motion:touchRegionId="@id/page"
你的锚点区域;motion:dragDirection="dragLeft"
你朝哪个方向滑动触发motion:onTouchUp="autoComplete"
触摸结束之后的动画
实现的效果如下:

-
其他属性:
motion:dragScale
:控制目标视图的滑动距离和用户手指滑动距离的相对比例,默认值是1。通俗来讲,取值小于1时view比手滑动的慢,取值大于1时view比手快;
motion:maxVelocity
:目标view的最大速度;
motion:maxAcceleration
:目标view的最大加速度;
motion:onTouchUp
属性其他可选值:
stop
(停止动画)、autoComplete
(自动完成动画)、autoCompleteToEnd
(自动完成到结束状态)、autoCompleteToStart
(自动完成到开始状态)、decelerate
(减速停止动画)、decelerateAndComplete
(减速并完成动画);
KeyFrameSet
- 介绍:
<KeyFrameSet>
就是提供声明更加复杂动画的元素,他声明了动画运动轨迹的关键点集合,平滑的连接这些关键点,可以得到动画;
通俗来讲:它是一个定义关键帧的容器,通过关键帧,可以精确定义动画的轨迹和行为;
- 代码
java
<KeyFrameSet>
<KeyPosition>
</KeyPosition>
<KeyAttribute>
</KeyAttribute>
</KeyFrameSet>
它有很多关键帧类型,每种类型有不同的属性,触发不同的事件,这个关键帧通过动画的时间轴(frameposition
)来定位;
关键帧`
KeyPosition
-
介绍:是一个关键帧类型,用于定义视图在动画路径上的位置和大小变化;
-
代码:
java<KeyPosition motion:framePosition="40" motion:keyPositionType="parentRelative" motion:motionTarget="@+id/TODO" motion:percentX="0.45" motion:percentY="0.75" > </KeyPosition>
-
属性:
motion:framePosition="40"
定义了动画帧的位置,值为0-100,代表在动画过程中的百分比位置;motion:motionTarget="@+id/TODO"
目标viewmotion:percentX="0.45
"取值在-1 - 1之间,坐标轴取决于:keyPositionType
motion:keyPositionType="parentRelative"
计算关键帧的位置,有三种计算方式:
三种计算方式:
第一种
parentRelative
:相当于父容器来计算关键帧,左上角为(0,0);

go
第二种:`deltaRelative`: 基于相对于起始点和结束点的相对偏移量来计算路径,起始坐标和终点坐标分别为(0,0)和(1,1)

第三种:以路径为x轴,以垂直方向为Y轴

KeyAttribute
-
介绍:
<KeyAttribute>
元素是用来在运动序列的特定时刻,设置视图的任何标准属性;通俗来讲:
KeyPosition
是规定view的位置和大小,KeyAttribute
是规定view的UI元素属性的关键帧类型;可以在动画的某些时刻对元素的多个属性进行变换,例如透明度、旋转、缩放、平移等,从而实现复杂的动画效果; -
代码:
java
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="50"
android:scaleX="1.5"
android:scaleY="1.5"
android:rotation="359"
>
</KeyAttribute>
-
属性:
motion:motionTarget="@+id/TODO"
目标viewmotion:framePosition="50"
关键帧在动画进度中的位置android:scaleX="1.5"
表示将view的weight扩大到1.5倍android:scaleY="1.5"
同理android:rotation="359"
将view旋转359度
-
其他属性:
-
android:alpha
设置透明度 -
android:translationX
、android:translationY
:控制视图的 X、Y 方向偏移; -
android:transformPivotX
、android:transformPivotY
:控制旋转和缩放的中心点; -
transformPivotTarget
: 指定其他视图作为旋转和缩放的中心点; -
motion:transitionEasing="easeInOut"
设置速度取值:加速(
ease-in
)减速(ease-out
)先快后慢(easeInOut
)
-
有这样的效果:

代码如下:
java
<?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"
motion:duration="2000">
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/middle"
>
<OnClick
motion:targetId="@+id/TODO"
motion:clickAction="transitionToEnd">
</OnClick>
<KeyFrameSet>
<KeyPosition
motion:framePosition="40"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.45"
motion:percentY="0.75"
>
</KeyPosition>
<KeyPosition
motion:framePosition="50"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.5"
motion:percentY="0.5"
>
</KeyPosition>
<KeyPosition
motion:framePosition="60"
motion:keyPositionType="parentRelative"
motion:motionTarget="@+id/TODO"
motion:percentX="0.65"
motion:percentY="0.45"
>
</KeyPosition>
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="50"
android:scaleX="1.5"
android:scaleY="1.5"
android:rotation="359"
>
</KeyAttribute>
<KeyAttribute
motion:motionTarget="@+id/TODO"
motion:framePosition="100"
android:scaleX="1"
android:scaleY="1"
android:rotation="360"
>
</KeyAttribute>
</KeyFrameSet>
</Transition>
<ConstraintSet android:id = "@+id/start">
<Constraint
android:id = "@+id/TODO"
android:layout_height="100dp"
android:layout_width="100dp"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintBottom_toBottomOf="parent"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp">
</Constraint>
</ConstraintSet>
<ConstraintSet android:id = "@+id/middle">
<Constraint
android:id = "@+id/TODO"
android:layout_height="100dp"
android:layout_width="100dp"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp">
</Constraint>
</ConstraintSet>
</MotionScene
动态设置
我们也可以在代码中动态设置
方法 | 功能 |
---|---|
transitionToEnd() | 触发当前 Transition 到 end 状态 |
transitionToStart() | 触发当前 Transition 到 start 状态 |
setProgress(float progress) | 直接设置动画进度,0~1 |
getProgress() | 获取当前 progress |
setTransition(int startId, int endId) | 动态切换当前 Transition |
示例:
java
MotionLayout motionLayout = findViewById(R.id.motionLayout);
// 手动设置动画进度到一半
motionLayout.setProgress(0.5f);
// 动态切换 Transition
motionLayout.setTransition(R.id.start, R.id.end);
// 播放动画到结束
motionLayout.transitionToEnd();
好啦,本次介绍到此结束,谢谢大家;VV