MotionLayout 是ConstraintLayout 2.0 新增的类,指在帮助Android开发者管理应用的中的motion和widget动画.
Why MotionLayout ?
The Android framework already provides several ways of adding animation in your application:
目前Android框架已经提供了如下几种添加动画到应用中的方式:
此节将聚焦于 MotionLayout 于其他方案的异同点.
MotionLayout,正如名字所示,首先是一个layout,承载不同的元素. 并且其也是ConstraintLayout的子类,是基于其强大的布局能力上构建的.
MotionLayout 是为了消除 layout transitions 和复杂的 motion 动画处理之间的差别而创建的. 可以认为具有一种兼具property aniamtion framework, TransitionManager和CoordinatorLayout混合起来的能力.
A mix between the property animation framework, layout transitions with TransitionManager, and CoordinatorLayout(多功能模块混合一起)
MotionLayout 通过在两个layouts间描述transition(类似TransitionManager), 但是能animate任意的属性(不仅仅是layout属性). 而且,其本质上是支持可查找的(seekable)transitions,就像 CoordinatorLayout(能单纯的通过触摸和transition驱动,实时移动到任意点). 支持触摸处理和keyframes, 运行开发者根据自身需要轻易的自定义transitions.
MotionLayout is fully declarative(完全声明式)
另外的一个关键的不同就是 MotionLayout 是完全 声明式的(declaractive) - 完全可以在xml中描述复杂的transition - 不需要任意代码(如果需要用代码表达motion,当前的属性动画框架已经提供了一个很好的方式实现).
MotionLayout tooling(工具管理)
我们相信此基于声明式的动效表达式能简单的创建动效, 并且提供了一个在Android Studio的图形工具去管理.
此工具目前还未ok,一旦ConstraintLayout 2.0 迭代到 stable或beta 后将可用
最终, 此工具会做为ConstraintLayout 2.0的一部分发布, 将作为支持库存在, 向后兼容到 API level 18 (JellyBean MR2) : 意味着95%的Android设备.
当前限制
MotionLayout 只能作用于于其直接children, 而TransitionManager,可用支持nested的布局层级和Activity Transitions.
MotionLayout
添加到gradle文件中:
dependencies {
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'
}
The main difference between ConstraintLayout and MotionLayout at the XML level is that the actual description of what MotionLayout will do is notnecessarily contained in the layout file.
ConstraintLayout和MotionLayout在xml层的主要不同点在于MotionLayout真正的描述信息不必要包含在layout文件中,而是将所有的信息放置在另外被引用到的xml文件中(MotionScene
). 如下所示:
<android.support.constraint.motion.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:id="@+id/motionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_01"
tools:showPaths="true">
<View
android:id="@+id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@color/colorAccent"
android:text="Button"
tools:layout_editor_absoluteX="147dp"
tools:layout_editor_absoluteY="230dp" />
</android.support.constraint.motion.MotionLayout>
scene_01.xml 是真正的描述文件(单独放置在 res/xml 文件夹目录下):
<MotionScene
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetStart="@layout/motion_01_cl_start"
motion:constraintSetEnd="@layout/motion_01_cl_end"
motion:duration="1000">
<OnSwipe
motion:touchAnchorId="@+id/button"
motion:touchAnchorSide="right"
motion:dragDirection="dragRight" />
</Transition>
</MotionScene>
�
此原理的灵感来源于ConstraintSets的Keyframes Animaitons, 如果对比不了解可以参考下 Keyframes Animations With ContraintLayout and ConstraintSets的视频
MotionLayout 属性
app:layoutDescription=”reference”
MotionScene XML文件的引用id值app:applyMotionScene=”boolean”
使用或者不使用MotionScene[default=true]app:showPaths=”boolean”
是否显示path [default=false]app:progress=”float”
指定transition进度[0,1]app:currentState=”reference”
强制指定当前的ConstraintSet
MotionScene
正如上诉提到的,跟普通的layouts不同,MotionLayout的具体信息描述都是放置在一个单独的XML文件中,也就是 MotionScene,存放在res/xml
文件夹下.
MotionScene的整体设计如下:
MotionScene 文件可以包含素有其所有想表达的动画:
ConstraintSets 的使用
ConstraintSets 之间的transtions操作
keyframes, touch 处理等等
motion_01_cl_start.xml 内容如下(按钮垂直屏幕居中在左侧):
<android.support.constraint.ConstraintLayout 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">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
motion_01_cl_end.xml 内容如下(按钮垂直屏幕居中在右侧):
�
<android.support.constraint.ConstraintLayout 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">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
�剖析下其中的关键点
四个layouts
MotionLayout初始化描述的xml文件(motion_01_basic.xml),其中的View属性可随意填写,因为其真实的描述信息并不是在这个文件中
MotionSence的描述文件(scene_01.xml),包含了起始和结束点的布局信息
起始位置的布局文件(motion_01_cl_start.xml)
结束位置的布局文件(motion_01_cl_end.xml)
其中直接childe的id必须是相同的(如上代码中的 button id)
OnSwipe handler
回看scene_01.xml布局详情定义中,在Transition
中定义了一个OnSwipe
handler, 这个的作用是用于通过手势的变动驱动transition.
如上图所示的,其核心属性包含如下:
dragDirection:追踪手势动画拖拽方向 (
dragRight / dragLeft / dragUp / dragDown
主要是定义[0,1]之间的进度值的设置),如dragLeft
值关心往左方向滑动才会被追踪touchAnchorId:追踪手势的对象 在此是 @+id/button
touchAnchorSide: 追踪手势对象的某一边?(
right / left / top / bottom
) 暂时未完全理解maxVelocity: 最大的速率控制
Self-Contained MotionScene
MotionLayout 同样支持在 MotionScene 文件中直接定义 ConstraintSets, 也是res/xml
放在文件夹中, 这样做有几大好处:
单个文件维护多个ConstraintSets
增加处理其他属性和自定义属性的功能
预想未来: 即将发布的在Android Studio中的MotionEditor将很可能只支持self-contained MotionScene文件
Interpolated Attributes(属性的截断)
MotionScene文件中定义的ConstraintSets中的Attributes(属性)不仅仅只是普通的layout属性, 除了position和bounds外,下列各种属性也会自动被MotionLayout拦截处理.
alpha
visibility
elevation
rotation
,rotation[X
/Y]
translation[X
/Y
/Z]
scaleX
/Y
* @see #CustomAttribute_attributeName
* @see #CustomAttribute_customBoolean
* @see #CustomAttribute_customColorValue
* @see #CustomAttribute_customDimension
* @see #CustomAttribute_customFloatValue
* @see #CustomAttribute_customIntegerValue
* @see #CustomAttribute_customStringValue
ConstraintSets使用举例:属性相关的设置文章二有讲
<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:interpolator="linear">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/button"
motion:touchAnchorSide="right" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/button"
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">
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#D81B60" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/button"
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">
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#9999FF" />
</Constraint>
</ConstraintSet>
</MotionScene>