原文连接html
MotionLayout 是一个来自 ConstraintLayout 2.0 的专一于动画的新布局。本系列的前几篇文章对该系统进行了很好的概述。我强烈建议你在阅读本文前先去查看它们。android
MotionLayout 动画系统经过在两种状态之间插入值(一般是控件的位置/大小)来工做,这些值是使用 ConstraintLayout 的约束系统 (ConstranitSets) 以及视图属性来指定的。这两种状态之间的转换也能够彻底由触摸事件驱动。这个系统一般会为你的过渡提供很好的效果。git
除了上面说的状态以外,MotionLayout 还支持关键帧(在本系列的第二部分中简单介绍过),咱们将在本文中深刻介绍这些关键帧。注意,虽然关键帧很好,可是它绝对是一个更专业的工具;你可能不须要或者偶尔才会用到。github
请记住,在应用中添加的动画应该有它的意义;不要滥用!工具
可是,若是须要对你的过渡效果添加额外的功能,那么关键帧能够帮助你扩展 MotionLayout 的功能。如你所见,这里有不少内容须要覆盖:布局
从较高的层次上看,关键帧能够对你的两个状态之间的插值进行一个修改。动画
MotionLayout 支持不一样的关键帧:google
KeyPosition
KeyAttribute
KeyCycle
KeyTimeCycle
注意,每种类型的关键帧都是独立于其余类型的关键帧的——也就是说,你不须要在相同的点上定义全部的关键帧(可是你不能在相同的点上定义相同类型的多个关键帧)spa
全部关键帧(位置、属性、循环、周期)都有一些关键的通用属性:3d
motion:framePosition
: 关键帧在过渡中(从0到100)的做用时机motion:target
: 哪一个对象受该关键帧影响motion:transitionEasing
: 使用哪一种插值器(默认为线性)motion:curveFit
: 样条(默认)或线形——使用哪一个曲线拟合关键帧。默认状况下是单调样条曲线,这使得过渡更加平滑,固然你也能够决定使用线性 (linear) 拟合。位置关键帧多是你最常使用到通用关键帧。它容许你修改控件在过渡期间在屏幕上的路径。举例,让咱们在 MotionLayout 中为其中的一个控件作动画:
咱们有一个起始状态(左下)和结束状态(右上),过渡过程就是控件在这两种状态之间的线性 (linear interpoltion) 直线运动。
经过引入位置关键帧,咱们能够将运动路径变成曲线运动:
添加更多的关键帧容许你建立复杂的运动路径。
<KeyFrameSet>
<KeyPosition motion:keyPositionType="pathRelative" motion:percentX="0.75" motion:percentY="-0.3" motion:framePosition="25" motion:target="@id/button"/>
<KeyPosition motion:keyPositionType="pathRelative" motion:percentY="-0.4" motion:framePosition="50" motion:target="@id/button"/>
<KeyPosition motion:keyPositionType="pathRelative" motion:percentX="0.25" motion:percentY="-0.3" motion:framePosition="75" motion:target="@id/button"/>
</KeyFrameSet>
复制代码
若是 ConstraintSets 已经容许你以很是灵活的方式摆放控件,那么你也许会问本身定位关键帧的意义是什么。缘由以下:
注意:在一个 MotionScene 中定义多个 ConstraintSets 是有可能的,因此若是你有一个多步骤的动做,其中这些步骤是有效的“静止”状态,那么你可使用它们而不是关键帧。状态到状态的转换必须在代码中完成(可使用改动监听器(change listeners))。
关键帧存在于 <KeyFrameSet>
属性中,<KeyFrameSet>
则存在于 MotionScene 文件中的 <Transition>
,而且至少包含:
target
: 被关键帧影响的控件framePosition
: 关键帧使用时机,(0-100)keyPositionType
: 所使用的坐标系 相对父容器(parentRelative), 三角定位(deltaRelative), 相对路径(pathRelative)
percentX / percentY
:位置的 (x,y) 坐标<Transition ...>
<KeyFrameSet>
<KeyPosition motion:keyPositionType="parentRelative" motion:percentY="0.25" motion:framePosition="50" motion:target="@+id/button"/>
</KeyFrameSet>
</Transition>
复制代码
在 MotionLayout 中的起始状态和结束状态容许复杂的定位。对于 ConstraintSets,它们可使用 ConstraintLayout 的全部功能。系统将根据密度 (density) 、屏幕方向(screen orientation)、语言(language) 等变化,正确的处理这些状态。
要使关键帧在这样的系统中发挥做用,咱们须要它们可以以相似自适应的方式进行布局——而不是简单的使用固定的位置。
为了解决这个问题,同时保持关键帧系统的轻量级,咱们提出了一种灵活的方法——在给定的坐标系中,每一个关键帧的位置用(x,y)坐标对 (pair) 表示:
motion:percentX=”<float>”
motion:percentY=”<float>”
这个坐标的含义取决于所使用的坐标系类型:parentRelative
, deltaRelative
, or pathRelative
.
注意:每一个关键帧的位置都是单独存在——每一个关键帧的位置均可以用它们本身相对的坐标系表示。
坐标是根据相对父容器表示的。这是一种很是直接和直观的方式来表达关键帧的位置,一般就足够了。一般状况下,你用它来作与父容器相关的大范围运动。
因为这个坐标系只基于父容器维度,而不是移动的控件的开始/结束位置,您可能会遇到这样的状况,即最后的关键帧位置以次优位置结束(相对于开始/结束位置)。(原文: you may encounter situations where the resulting keyframe position ends in a suboptimal position (relative to the start/end positions).)
第二个坐标系经过使用开始/结束位置定义来解决这个问题。坐标表示起点和终点之间的百分比。
和相对父容器坐标系有点像,这是一个相对直观的坐标系统,通常也会给出很好的结果。当你但愿控件以水平或垂直运动开始或结束时,它十分有用。
它有一个潜在的问题——由于它是根据控件从开始到结束位置之间的差别定义的额,若是差别很是小(或者没有)关键帧将不会在对应轴上发生变化。例如,若是控件在屏幕从左向右移动,而保持在相同的高度,那么对位置关键帧使用 deltarelative
percentY
将不会产生任何效果。
最后一个坐标系定义了一个相对于从开始状态到结束状态的直线路径。它能够解决 deltaRelative 坐标系中的问题——当一个控件没有在垂直轴移动的状况下,使用 pathRelative 将容许将位置关键帧设置为偏离路径。注意,它也支持负坐标。它是一个更特殊的坐标系,可是在处理时间上特别有用。下面有一个例子是实现一个曲线形状(好比“S”形),即便端点发生变化,它也会保持不变。
在 Material Design 中使用的一种典型的运动类型是圆弧运动(arc motion)。使用 MotionLayout 建立圆弧运动的一种方法是在起始位置和结束位置之间添加正确放置的位置关键帧,如前一节所述。
在 ConstraintLayout 2.0.0 alpha 2 中,咱们引入了一种实现完美元弧运动的新方案——并且它更加容易使用。你只须要将motion:pathMotionArc
属性添加到起始的 ConstraintSet ,从而让默认的线形运动 (linear motion) 切换到弧线运动 (arc motion) 。
让咱们来看一个简单的例子,开始状态是屏幕的右下,结束的位置是屏幕的顶部而且水平居中。添加下面这个属性就能够产生弧线运动:
motion:pathMotionArc=”startHorizontal”
若是把属性换成:
motion:pathMotionArc=”startVertical”
就会改变弧线的方向:
你仍然可使用位置关键帧来创造更复杂的弧线路径。下面是结果:
它是经过在动画中添加一个垂直居中的位置关键帧来实现的:
<KeyPosition motion:keyPositionType="parentRelative" motion:percentY="0.5" motion:framePosition="50" motion:target="@id/button"/>
复制代码
经过设置 motion:pathMotionArc
属性,还能够在该场景中使用关键帧来更改圆弧的方向。属性能够是flip
(翻转当前的圆弧方向)、none
(还原为线性运动),也能够是startHorizontal
或startVertical
。
<KeyPosition motion:keyPositionType="parentRelative" motion:pathMotionArc="flip" motion:percentY="0.5" motion:framePosition="50" motion:target="@id/button"/>
复制代码
<KeyPosition motion:keyPositionType="parentRelative" motion:pathMotionArc="none" motion:percentY="0.5" motion:framePosition="50" motion:target="@id/button"/>
复制代码
在前几节中,咱们介绍了各类机制帮助你定义一个运动路径。对于一个动画,不只仅须要选择合适的路径;时间也是相当重要的。
因为位置关键帧能够由时间指定,你可使用它们来定义控件移动的快慢,具体取决于移动的空间。
可是在一个单独片断内——开始和结束状态之间,或者在关键帧之间——时间插值器是线形的。(the time interpolation is linear)
你可使用motion:transitionEasing
属性来指定一个缓和曲线来修改它。你能够将这个属性使用在ConstraintSets
或 关键帧,它接受这些值:
cubic(float, float , float, float)
, x1,y1,x2,y2 表示一个从 0,0 到 1,1 的三次贝塞尔方程的控制点standard
, accelerate
, decelerate
, 预约义的曲线相似 Material Design definitions.一般用于在非触摸驱动的动画中。它最适合于开始和结束都是静止状态的元素。
加速一般用于一个元素移出屏幕。
减速一般用于一个元素进入屏幕。
属性关键帧容许你在动画过程当中指定控件的属性在给定时间点的更改——换句话说,它们与位置关键帧相似,但做用于属性而不是位置。
上面的例子经过在 MotionScene 文件中添加 KeyAttribute 元素来实现:
<KeyFrameSet>
<KeyAttribute android:scaleX="2" android:scaleY="2" android:rotation="-45" motion:framePosition="50" motion:target="@id/button" />
</KeyFrameSet>
复制代码
相比于KeyPostion
,咱们须要指定framePosition
(关键帧应用时机)和目标(哪一个对象受到影响)。
一些你开箱即用的 View 属性:
android:visibility
, android:alpha
, android:elevation
, android:rotation
, android:rotationX
, android:rotationY
, android:scaleX
, android:scaleY
, android:translationX
, android:translationY
, android:translationZ
受到应用程序的 SDK level 限制,其中一些属性将不起做用:
android:elevation
android:translationZ
你能够经过添加 <CustionAttribute>
子节点在 ConstraintSets 和 KeyAttribute 节点中声明自定义属性。这个节点须要一个属性名(attributeName
),它是getter/setter
的名称(除去set/get前缀)和要传入或使用的值类型,属性类型指定为下方其中一个:
customColorValue
: 颜色值customColorDrawableValue
: 颜色值 DrawablecustomIntegerValue
: IntegercustomFloatValue
: FloatcustomStringValue
: StringcustomDimension
: 尺寸customBoolean
: Boolean举例,这面是对应上面动画的 XML:
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@+id/button" ...>
<CustomAttribute motion:attributeName="backgroundColor" motion:customColorValue="#D81B60"/>
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@+id/button" ...>
<CustomAttribute motion:attributeName="backgroundColor" motion:customColorValue="#9999FF"/>
</Constraint>
</ConstraintSet>
复制代码
本文介绍了 MotionLayout 中 最多见的关键帧和路径规范。咱们将在本系列的第五部分讨论 循环(KeyCycle)和 周期(KeyTimeCycle),它们介绍了一种很是强大的方法,能够将扰动(相似波形)添加到属性(基于路径或基于时间),容许各类有趣但可预测的循环效果(反弹(bounce)、抖动(shaking)、脉动(pulsations)等)。
使用 MotionLayout 的各类示例能够在 ConstraintLayout examples github repository查看。