Android MotionLayout动作交错 [英] Android MotionLayout motionStaggered
问题描述
我正在尝试使用MotionLayout来激活我的视图,但是希望某些约束先于其他约束进行动画.我认为这是该议案的目的:过渡的交错属性,但我不知道它是如何工作的,也没有任何实例可以成功地在任何地方使用.使用MotionLayout的更新版本,似乎我们应该为各个约束设置motion:motionStagger,但是同样,我似乎无法使它按照需要错开.我只能找到的文档是此处解释了增强版交错的API,但我不知道如何使用它.
我在下面添加了我的MotionLayout代码.供参考,我使用的是ConstraintLayout的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>
好吧,所以在解决了很长时间之后,进行了很多次尝试和错误,并研究了 您会注意到,我将持续时间增加到1000,以便更清楚地看到交错(一旦您弄清楚了交错值,可以在此处更新持续时间,并且交错应适当缩放以适合时间范围). /p>
确定单个视图的标记值: 因此,现在我们需要弄清楚该加什么?在 这是数学变得非常复杂的地方.对于我们要设置交错的每个视图,都应按交错值对其进行排序.给出的方程式(经过修改使其比文章更易读)为: 这肯定有点复杂,但是我可以用我的例子来分解. 因此,在我的示例中,我们已经讨论了我如何希望对三个视图进行一定程度的均匀交错(这就是为什么我们选择0.6的交错值)的原因.我知道,根据下面的方程式的逆结构,具有最大 比方说,我们有三个视图,一个我想首先进入的ImageView,一个我想进入第二个的TextView,一个我想进入第三个的Button.因此,我将为ImageView分配一个motionStagger值为3,为TextView分配一个motionStagger值为2,为TextView分配一个motionStagger值为1.让我们在这里进行计算: 因此,ImageView会从0开始动画,并动画400毫秒(如上一节所示).
现在让我们为TextView计算 因此TextView在300处开始动画,并在400ms内进行动画. 最后,让我们计算一下Button的开始时间: 因此,Button在600处开始动画,并动画400毫秒. 这些值可以根据您选择的MotionStagger值进行移动和交错.为了说明起见,我试图使它尽可能简单,但是根据您要实现的目标,它可能会变得非常复杂.这是我上面概述的示例的最终代码. 在这里,您将需要另一个并行的 I am trying to get my views to animate using MotionLayout but want certain Constraints to animate in before others. I think this was the purpose of the motion:staggered property for Transition but I don't understand how that works and there are no examples of it successfully working anywhere. With updated versions of MotionLayout it seems we should have motion:motionStagger for individual Constraints but again I cannot seem to get it to stagger as desired. Only documentation I could find was here explaining the Enhanced Staggered API but I don't understand how to use it. I have added my MotionLayout code below. For reference, I am using the Ok, so after messing around with this for a long time, a lot of trial and error, and studying the equations given in this release update, this is what I have come up with. The linked article above gives us some somewhat confusing equations which are DETERMINING TRANSITION STAGGER VALUE: To determine what you want the overall stagger to be, think about the number of views you are trying to stagger.
The article I linked above states that the You'll notice that I increased the duration to 1000 to see the stagger more clearly (Once you figure out your stagger values, the duration here can be updated and the stagger should scale appropriately to fit within the time frame). DETERMINING INDIVIDUAL VIEWS STAGGER VALUES: So now we need to figure out what to put as the ? in This is where the math gets very complicated. For each view that we are going to set a stagger on, they should be ordered by stagger value. The equation (modified to make slightly more readable than the article) we are given is: This is definitely a little complicated but I can break it down with my example. So for my example we already talked about how I have three views that I want to stagger somewhat evenly (which is why we chose a stagger value of 0.6). I know based on the inverse structure of the equation below that the view with the highest Let's say we have three views, an ImageView that I want to come in first, a TextView that I want to come in second, and a Button that I want to come in third. So I will assign the ImageView a motionStagger value of 3, the TextView a motionStagger value of 2, and the TextView a motionStagger value of 1. Let's do the calculations here: So the ImageView starts animating at 0 and animates for 400ms (as shown in the above section).
Now let's calculate for the TextView So the TextView starts animating at 300 and animates for 400ms. Finally, let's calculate the start time for the Button: So the Button starts animating at 600 and animates for 400ms. These values can be shifted and staggered based on what you chose as your motionStagger values. I tried to make this as simple as possible for explanation sake but it can get very complicated depending on what you are trying to accomplish. Here is what the final code will look like for the example I outlined above. Where you will need another parallel 这篇关于Android MotionLayout动作交错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!<Motion motion:motionStagger="?" />
animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))
motionStagger
值的视图将首先进行动画处理. 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
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
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
<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>
ConstraintSet
作为结束状态. 2.0.0-beta3'
version of ConstraintLayout<?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>
Let
The motionStagger value is S(Vi)
The overall stagger value is stagger (from 0.0 - 1.0)
The duration of the animation is duration
The views animation duration = duration * (1 - stagger)
The view starts animating at duration * (stagger - stagger * (S(Vi) - S(V0)) / (S(Vn) - S(V0)))
viewDuration = totalDuration*(1 - stagger)
so we can rearrange this equation to become stagger = 1 - (viewDuration / totalDuration)
. In my case, since I want to have three different moments when views enter, I want to have my viewDuration / totalDuration
to be about 1/3
. To simplify the math, I have chosen to have my stagger as 0.6
, making each viewDuration 400. So my transition code looks like the following <Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
motion:duration="1000"
motion:motionInterpolator="easeInOut"
motion:staggered="0.6" />
<Motion motion:motionStagger="?" />
animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))
motionStagger
value will animate in first. 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
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
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
<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>
ConstraintSet
for the end state.