Android MotionLayout动作交错 [英] Android MotionLayout motionStaggered

查看:135
本文介绍了Android MotionLayout动作交错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用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>

确定单个视图的标记值:

因此,现在我们需要弄清楚该加什么?在<Motion motion:motionStagger="?" />

这是数学变得非常复杂的地方.对于我们要设置交错的每个视图,都应按交错值对其进行排序.给出的方程式(经过修改使其比文章更易读)为:

animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))

这肯定有点复杂,但是我可以用我的例子来分解.

因此,在我的示例中,我们已经讨论了我如何希望对三个视图进行一定程度的均匀交错(这就是为什么我们选择0.6的交错值)的原因.我知道,根据下面的方程式的逆结构,具有最大motionStagger值的视图将首先进行动画处理.

比方说,我们有三个视图,一个我想首先进入的ImageView,一个我想进入第二个的TextView,一个我想进入第三个的Button.因此,我将为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处开始动画,并在400ms内进行动画.

最后,让我们计算一下Button的开始时间:

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

因此,Button在600处开始动画,并动画400毫秒.

这些值可以根据您选择的MotionStagger值进行移动和交错.为了说明起见,我试图使它尽可能简单,但是根据您要实现的目标,它可能会变得非常复杂.这是我上面概述的示例的最终代码.

<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作为结束状态.

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 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>

解决方案

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

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)))

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 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" />

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 <Motion motion:motionStagger="?" />

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:

animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))

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 motionStagger value will animate in first.

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:

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

So the ImageView starts animating at 0 and animates for 400ms (as shown in the above section). Now let's calculate for the 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

So the TextView starts animating at 300 and animates for 400ms.

Finally, let's calculate the start time for the Button:

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

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.

<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>

Where you will need another parallel ConstraintSet for the end state.

这篇关于Android MotionLayout动作交错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆