ConstraintLayout 2.0 经过漫长时间的迭代终于发布了正式版。MotionLayout是这次更新的重要功能。MotionLayout 是 ConstraintLayout的子类,所以可以直接使用ConstraintLayout的各种特性,此外MotionLayout 的推出极大提高了动画开发的便利。
基本概念
使用 MotionLayout 之前需要对一些基本概念作一些介绍。
只需要描述动画开始和结束的状态,MotionLayout就可以帮我们从开始状态过度到结束的状态。
由于MotionLayout是ConstraintLayout的子类,所以最外层的布局直接改成MotionLayout,里面的 view 不需要做任何变动,此外会多出一行 app:layoutDescription=“@xml/my_motion_scene",这里指定了的 xml 文件描述了动画相关的一些信息。左右侧的ConstraintSet指定了开始和结束的状态,每个ConstraintSet里面可以指定多个Constraint,每个Constraint的作用是对相应的 view 状态进行描述。
ConstraintSet
下面看一下ConstraintSet如何编写
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@+id/button1">
<Layout
android:layout_marginTop="20dp"
motion:layout_constraintLeft_toLeftOf="parent"
motion:layout_constraintRight_toRightOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Transform
android:rotationX="20"
android:translationX="100dp" />
<PropertySet
android:alpha="0.5"
android:visibility="visible" />
<CustomAttribute
motion:attributeName="backgroundColor"
motion:customColorValue="@color/colorPrimary" />
<Motion />
</Constraint>
<Constraint android:id="@+id/button2">
...
</Constraint>
</ConstraintSet>
这里的ConstraintSet指定了两个Constraint,两个Constraint分别描述了 button1和button2 的状态,Constraint里面有五类标签
- <Layout> 布局相关
- <Transform> 换相关,旋转,平移,缩放...
- <PropertySet> 透明度和可见性
- <CustomAttribute>定义属性,类似于属性动画,会去寻找对用属性的 getXxx() setXxx() 方法
- <Motion>
Transition
有了开始和结束的状态后,还需要Transition 对动画做一些控制,下面看一下Transition的写法
<Transition
motion:constraintSetEnd="@id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<OnClick motion:targetId="@+id/button" />
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:dragDirection="dragUp"
motion:touchAnchorSide="bottom" />
<KeyFrameSet></KeyFrameSet>
</Transition>
constraintSetStart和constraintSetEnd分别指定分别指定了动画开始和结束时的ConstraintSet,duration指定动画执行时间,此外Transition里面还支持三类标签
- <OnClick> 指定点击触发动画的控件
- <OnSwipe> 滑动触发动画
- touchAnchorId 指定滑动哪个控件触发动画
- dragDirection 指定往哪个方向滑动触发动画,这里指定为向上滑动触发动画
- touchAnchorSide 指定滑控件的哪一边触发动画,这里指定为控件的底部
合起来看 button的底部向上滑触发动画
- 关键帧 ,后面介绍
案例 1
下面看一个简单的例子
动画开始是 按钮位于最左边 ,结束后位于右侧。 首先需要写一个MotionLayout,期中layoutDescription指定为scene_02这个文件
<androidx.constraintlayout.motion.widget.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:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_02"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:text="Button"
tools:layout_editor_absoluteX="147dp"
tools:layout_editor_absoluteY="230dp" />
</androidx.constraintlayout.motion.widget.MotionLayout>
再看下scene_02.xml文件的写法
<MotionScene>
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/button"
motion:touchAnchorSide="right" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/button">
<Layout
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"//开始 父布局左侧
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/button">
<Layout
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"//结束 父布局右侧
motion:layout_constraintTop_toTopOf="parent" />
</Constraint>
</ConstraintSet>
</MotionScene>
这里注意第 19 行,指定开始时靠父布局的左侧对齐;第 30 行,指定结束时靠父布局的右侧侧对齐。只需要在 xml里面指定开始和结束的状态,其他的全部交给MotionLayout帮我们实现。
案例 2
现在需求有点变化,需要从左滑到右侧时变化颜色。
<MotionScene>
<Transition>
...
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint ...>
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#D81B60" />//开始的颜色
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint ...>
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#9999FF" />//结束的颜色
</Constraint>
</ConstraintSet>
</MotionScene>
只需要在开始和结束的ConstraintSet里面的CustomAttribute指定颜色值即可。
案例 3
需求再次有点变化,从左滑到右侧并且中间进过一个点。
这时候就需要引入Keyframes(关键帧)的概念
Keyframes(关键帧)
MotionLayout支持动画过程中插入若干关键帧,这样就可以从开始进过这些关键帧,最终过渡到结束的状态。
关键帧写在Transition的KeyFrameSet里面,关键帧有很多类型,这里主要介绍两个常用的KeyPosition 和 KeyAttribute
KeyPosition
KeyPosition用于指定位置,说道位置不得不提到坐标系,MotionLayout为我们提供了三种坐标系,方便我们指定位置。
-
parentRelative parent指的是MotionLayout
-
deltaRelative 以控件开始和结束为基准的坐标系,原点位于控件开始的位置
-
pathRelative x 轴开始处为控件开始的位置,x轴结束的位置为控件结束的位置。
回到上面的动画,只需使用KeyPosition指定一个位置即可。
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<OnClick motion:targetId="@+id/button" />
<OnSwipe
motion:dragDirection="dragUp"
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="bottom" />
<KeyFrameSet>
<KeyPosition
motion:motionTarget="@id/button"
motion:framePosition="50"
motion:keyPositionType="pathRelative"
motion:percentY="-0.25"/>
</KeyFrameSet>
</Transition>
framePosition可以理解为动画执行时间,这里的含义是动画执行到一半时;keyPositionType指定坐标系为pathRelative;最后通过percentY指定位置。
案例 4
需求叕变了,这次动画执行到中间时候,需要放大控件,并且旋转一个角度
这时就需要KeyAttribute这种类型的关键帧了,
KeyAttribute
代码如下
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000">
<KeyFrameSet>
<KeyAttribute
android:rotation="-45"
android:scaleX="2"
android:scaleY="2"
motion:framePosition="50"
motion:motionTarget="@id/button" />
<KeyPosition
motion:framePosition="50"
motion:keyPositionType="pathRelative"
motion:motionTarget="@id/button"
motion:percentY="-0.3" />
</KeyFrameSet>
</Transition>
KeyAttribute中指定了动画执行到 一半时,放大一倍,且旋转 -45度。
Motion Editor
AS 在 4.0时为MotionLayout引入了一种可视化编辑器,使得开发者可以更加方便实现动画以及实时预览动画,通过操作Motion Editor可以生成上面手动编写的XML 文件
关于Motion Editor 的使用可以参考官方的这篇文章 Build animation with the Motion Editor
此外下面这篇文章手摸手教学,使用Motion Editor生成 twitter 闪屏页动画。 Android MotionLayout: Creating the Twitter splash screen in the simplest way possible
最后
此篇文章为MotionLayout介绍的第一篇,主要介绍一些基本使用。后面一篇会结合项目中某些场景谈一谈具体实践。