Android运动布局运动交错
我试图使用MotionLayout设置视图的动画,但希望在其他约束之前设置某些约束的动画。我认为这就是动议的目的:交错过渡属性,但我不明白它是如何工作的,而且在任何地方都没有成功工作的例子。在MotionLayout的更新版本中,似乎我们应该为单个约束设置motion:motionStagger,但我似乎无法使其按照所需设置为stagger。我能找到的唯一文档是解释增强的交错API,但我不知道如何使用它 我在下面添加了我的MotionLayout代码。作为参考,我使用的是ConstraintLayout的Android运动布局运动交错,android,animation,layout,motion,Android,Animation,Layout,Motion,我试图使用MotionLayout设置视图的动画,但希望在其他约束之前设置某些约束的动画。我认为这就是动议的目的:交错过渡属性,但我不明白它是如何工作的,而且在任何地方都没有成功工作的例子。在MotionLayout的更新版本中,似乎我们应该为单个约束设置motion:motionStagger,但我似乎无法使其按照所需设置为stagger。我能找到的唯一文档是解释增强的交错API,但我不知道如何使用它 我在下面添加了我的MotionLayout代码。作为参考,我使用的是ConstraintLa
2.0.0-beta3'
版本
<?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">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="300"
motion:motionInterpolator="easeInOut"
motion:staggered="0.4" />
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/translucentOverlay">
<Layout
android:layout_width="5dp"
android:layout_height="5dp"
motion:layout_constraintBottom_toBottomOf="@id/imageBorder"
motion:layout_constraintEnd_toEndOf="@id/imageBorder"
motion:layout_constraintStart_toStartOf="@id/imageBorder"
motion:layout_constraintTop_toTopOf="@id/imageBorder" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="0.0" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/imageBorder">
<Layout
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<CustomAttribute
motion:attributeName="crossfade"
motion:customFloatValue="0" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/imageBackground">
<Layout
android:layout_width="32dp"
android:layout_height="32dp"
motion:layout_constraintBottom_toBottomOf="@id/imageBorder"
motion:layout_constraintEnd_toEndOf="@id/imageBorder"
motion:layout_constraintStart_toStartOf="@id/imageBorder"
motion:layout_constraintTop_toTopOf="@id/imageBorder" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/profileInitialText">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="@id/imageBackground"
motion:layout_constraintEnd_toEndOf="@id/imageBackground"
motion:layout_constraintStart_toStartOf="@id/imageBackground"
motion:layout_constraintTop_toTopOf="@id/imageBackground" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="1.0" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/profileImage">
<Layout
android:layout_width="32dp"
android:layout_height="32dp"
motion:layout_constraintBottom_toBottomOf="@id/imageBackground"
motion:layout_constraintEnd_toEndOf="@id/imageBackground"
motion:layout_constraintStart_toStartOf="@id/imageBackground"
motion:layout_constraintTop_toTopOf="@id/imageBackground" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/name">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="128dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="0.0" />
<Motion motion:motionStagger="5" />
</Constraint>
<Constraint android:id="@id/description">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/name" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="0.0" />
<Motion motion:motionStagger="5" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/translucentOverlay">
<Layout
android:layout_width="match_parent"
android:layout_height="match_parent" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="1.0" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/imageBorder">
<Layout
android:layout_width="88dp"
android:layout_height="88dp"
motion:layout_constraintBottom_toBottomOf="@id/imageBackground"
motion:layout_constraintEnd_toEndOf="@id/imageBackground"
motion:layout_constraintStart_toStartOf="@id/imageBackground"
motion:layout_constraintTop_toTopOf="@id/imageBackground" />
<CustomAttribute
motion:attributeName="crossfade"
motion:customFloatValue="1" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/imageBackground">
<Layout
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginTop="64dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/profileInitialText">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintBottom_toBottomOf="@id/imageBackground"
motion:layout_constraintEnd_toEndOf="@id/imageBackground"
motion:layout_constraintStart_toStartOf="@id/imageBackground"
motion:layout_constraintTop_toTopOf="@id/imageBackground" />
<Motion motion:motionStagger="2" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="0.0" />
</Constraint>
<Constraint android:id="@id/profileImage">
<Layout
android:layout_width="70dp"
android:layout_height="70dp"
motion:layout_constraintBottom_toBottomOf="@id/imageBackground"
motion:layout_constraintEnd_toEndOf="@id/imageBackground"
motion:layout_constraintStart_toStartOf="@id/imageBackground"
motion:layout_constraintTop_toTopOf="@id/imageBackground" />
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/name">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/profileImage" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="1.0" />
<Motion motion:motionStagger="5" />
</Constraint>
<Constraint android:id="@id/description">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/name" />
<CustomAttribute
motion:attributeName="alpha"
motion:customFloatValue="1.0" />
<Motion motion:motionStagger="5" />
</Constraint>
</ConstraintSet>
交错排列的实际数学可能有点混乱,但在实践中 交错 正在设置动画的每个视图都会指定一个Stager值(应用程序:motionStagger) 默认情况下,视图的参差值是与视图列表中最顶部视图的曼哈顿距离。可以通过属性手动设置该值 这将为每个使用motionStagger标记的视图指定一个浮点参差值(未标记的视图将被忽略)。首先启动具有最低浮点值(V0)的视图。最后启动具有最高浮点值(Vn)的视图
- 对于交错值S(Vi)的任何视图
- 过渡交错值为TS(从0.0到1.0)
- 动画的持续时间为持续时间
- 视图动画持续时间DS=持续时间*(1-TS)
- 调用交错分数SFi=(S(Vi)-S(V0))/(S(Vn)-S(V0))
- 视图在:(持续时间DS)*SFi开始设置动画
The animation duration is 3.0 sec = 5 * (1- 0.4)
View1 stagger fraction = 0 = (2-2)/(7-2)
View1 starts at 0.0 sec
View1 end at 3.0 sec (0.0 + 3.0)
View2 stagger fraction = 0.6 = (5-2)/(7-2)
View2 starts at 1.2 sec (5.0-3.0) * 0.6
View2 ends at 4.2 sec 1.2 + 3.0
View3 stagger fraction = 1
View3 starts at 2.0 sec (5.0 - 3.0) * 1
View3 ends at 5.0 sec
好吧,在经历了很长一段时间的尝试和错误之后,研究了发布更新中给出的公式,这就是我所想到的 上面的链接文章给了我们一些令人困惑的方程,它们是
Let
运动参差值为S(Vi)
整体参差值为参差(从0.0到1.0)
动画的持续时间为持续时间
视图动画持续时间=持续时间*(1-交错)
视图在持续时间*(参差-参差*(S(Vi)-S(V0))/(S(Vn)-S(V0))开始设置动画。
确定过渡交错值:
要确定希望整体错开的位置,请考虑尝试错开的视图数量。
我上面链接的文章指出,viewDuration=totalDuration*(1-stagger)
因此我们可以将这个等式重新排列为stagger=1-(viewDuration/totalDuration)
。在我的例子中,因为我希望在视图进入时有三个不同的时刻,所以我希望我的viewDuration/totalDuration
大约是1/3
。为了简化计算,我选择将参差设置为0.6
,使每个视图的持续时间为400。因此,我的转换代码如下所示
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:motionInterpolator="easeInOut"
motion:staggered="0.6" />
这肯定有点复杂,但我可以用我的例子来说明
所以在我的例子中,我们已经讨论了如何有三个视图,我希望它们在某种程度上均匀地错开(这就是为什么我们选择0.6的错开值)。根据下面等式的逆结构,我知道具有最高motionStagger
值的视图将首先设置动画
假设我们有三个视图,一个我想排在第一位的ImageView,一个我想排在第二位的TextView,还有一个我想排在第三位的按钮。因此,我将为ImageView指定一个motionStagger值3,为TextView指定一个motionStagger值2,为TextView指定一个motionStagger值1。让我们在这里进行计算:
Stagger value = 0.6
motionStaggerValues = 3 (for ImageView), 2 (for TextView), and 1(for Button)
ImageView animationStartTime = 1000 * (0.6 - 0.6 * ((3-1)/(3-1)))
= 1000 * (0.6 - 0.6 * (1)) = 1000 * 0 = 0
因此,ImageView从0开始设置动画,并设置400毫秒的动画(如上一节所示)。
现在,让我们计算TextView
Stagger value = 0.6
motionStaggerValues = 3 (for ImageView), 2 (for TextView), and 1(for Button)
TextView animationStartTime = 1000 * (0.6 - 0.6 * ((2-1)/(3-1)))
= 1000 * (0.6 - 0.6 * (1/2)) = 1000 * 0.3 = 300
因此,TextView从300开始设置动画,并设置400毫秒的动画
最后,让我们计算按钮的开始时间:
Stagger value = 0.6
motionStaggerValues = 3 (for ImageView), 2 (for TextView), and 1(for Button)
TextView animationStartTime = 1000 * (0.6 - 0.6 * ((1-1)/(3-1)))
= 1000 * (0.6 - 0.6 * (0)) = 1000 * 0.6 = 600
因此,按钮在600开始设置动画,并在400毫秒内设置动画
这些值可以根据您选择的运动交错值进行移动和交错。为了便于解释,我试着让这个尽可能简单,但它可能会变得非常复杂,这取决于您试图完成的任务。下面是我上面概述的示例的最终代码
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/imageView">
...
<Motion motion:motionStagger="3" />
</Constraint>
<Constraint android:id="@id/textView">
...
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/button">
...
<Motion motion:motionStagger="1" />
</Constraint>
</ConstraintSet>
...
...
...
其中,您将需要另一个用于结束状态的并行约束集 这对数学分解很有帮助!我正在尝试实现它,但似乎无法在背景/图像之后获得要设置动画的名称/文本。我在问题中添加了我的运动场景代码。我遗漏了什么?有趣的是,我运行这个测试时,在我的视图上输入了精确的值,首先输入的是motionStagger设置为7的值,最后输入的是motionStagger设置为2的值。似乎你的数学在某个地方有点不对劲。谢谢你的帮助。分解它让我对如何进行有了更好的想法!是的,我可能把它倒过来了。这将是-0.4的行为。对于0.4的sta
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/imageView">
...
<Motion motion:motionStagger="3" />
</Constraint>
<Constraint android:id="@id/textView">
...
<Motion motion:motionStagger="2" />
</Constraint>
<Constraint android:id="@id/button">
...
<Motion motion:motionStagger="1" />
</Constraint>
</ConstraintSet>