使用MotionLayout可以实现不同的过渡效果吗?

13

我试图根据点击哪个按钮,在不同的位置显示一个视图。 但是,当我在其中使用两个<ConstraintSet>时,MotionScene无法正常工作。

<MotionScene xmlns:motion="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
>

<Transition android:id="@+id/toTimePicker2"
motion:duration="500" motion:constraintSetStart="@layout/add_event_fragment" 
 motion:constraintSetEnd="@id/enableTimePicker2">
<OnClick motion:target="@+id/time_to" motion:mode="toggle"/>
</Transition>

<Transition android:id="@+id/toTimePicker1"
motion:constraintSetStart="@layout/add_event_fragment" 
motion:constraintSetEnd="@id/enableTimePicker1"
motion:duration="500">
<OnClick motion:target="@+id/time_from"
  motion:mode="toggle" />
 < /Transition >

有没有人知道如何使用Motion Layout实现它? 看起来我应该在代码中使用ConstraintSet,但我只是想知道是否可以使用Motion实现


你解决了这个问题吗? - teh_raab
不,这种情况下使用ConstraintSet。 仍在努力弄清楚如何做到这一点。 - Slava Krasnov
3个回答

21

经过数个小时的尝试,我终于找到了为什么它对我不起作用的原因。我知道这一定是可能的,因为谷歌提供了一个具有多个转换的示例https://github.com/googlesamples/android-ConstraintLayoutExamples/blob/multi_state/motionlayout/src/main/res/xml/scene_26.xml。但无论我尝试什么,xml文件中只有第一个转换会起作用。最终我意识到,为了让一个转换起作用,UI的当前状态必须与约束集中定义的开始或结束状态匹配。不仅如此,但你不能在定义相同的约束集超过一次,也就是说,如果你有两个不同的转换可以发生在初始屏幕上,两个转换都需要共享约束集。

我创建了一个简单的示例,其中有两个移动并带有不同平移的按钮。我无法在任何地方找到这个简单的示例,所以希望这会对一些人有所帮助。正如您在视频中看到的那样,当“从左到右”的文本向右移动时,您无法将“从上到下”的文本移动到底部,因为该状态未在转换中定义。

最后,我遇到的最后一项注意事项是每个约束集似乎都必须包含作为转换部分的每个视图,无论它是否在当前转换中。否则,视图似乎只会随意移动。

enter image description here

以下是布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_main"
        app:layoutDescription="@xml/motion_scene"
        tools:context=".MainActivity">

    <TextView
            android:id="@+id/left_to_right_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Left to right"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    <TextView
            android:id="@+id/top_to_bottom_text"
            android:text="Top to bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.motion.widget.MotionLayout>

这是 MotionScene

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:app="http://schemas.android.com/apk/res-auto">


    <Transition
            app:constraintSetStart="@id/base"
            app:constraintSetEnd="@id/bottom">
        <OnClick
                app:targetId="@id/top_to_bottom_text">

        </OnClick>

    </Transition>

    <Transition
            app:constraintSetStart="@id/base"
            app:constraintSetEnd="@id/right">
        <OnClick
                app:targetId="@id/left_to_right_text">

        </OnClick>

    </Transition>


    <ConstraintSet android:id="@+id/base">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/bottom">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
        />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/right">

        <Constraint
                android:id="@id/left_to_right_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
                android:id="@id/top_to_bottom_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>
    </ConstraintSet>
</MotionScene>

那么你应该如何能够从上到下和从左到右移动呢?定义额外的转换吗? - Boy
@Boy 是的,我在我的答案中包含了带有两个转换的xml。 - odiggity

7
Google在其Google Developers中介绍了以下内容:注意:在MotionScene中可以定义多个ConstraintSets,因此如果您有一个多步运动,其中这些步骤是有效的“静止”状态,则可以使用它们代替关键帧。状态之间的转换必须在代码中完成(更改侦听器可用)。因此,我通过创建监听器来实现它:
motion_layout.setTransitionListener(
    object : MotionLayout.TransitionListener {
        override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
        }

        override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
            if (currentId == R.id.two) {
                //if the 1st transition has been made go to the second one
                intro_motion_layout.setTransition(R.id.two, R.id.three)
                intro_motion_layout.transitionToEnd()
            }
        }
    }
)
intro_motion_layout.transitionToEnd()

然后在我的MotionScene中,我有三个转换:


<?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:constraintSetStart="@+id/one"
    motion:constraintSetEnd="@+id/two"
    motion:duration="200"
    motion:interpolator="linear" />

<ConstraintSet android:id="@+id/one">
    <Constraint />
</ConstraintSet>

<ConstraintSet android:id="@+id/two">
    <Constraint />
</ConstraintSet>

<ConstraintSet android:id="@+id/three">
    <Constraint />
</ConstraintSet>


在onTransitionCompleted监听器中调用transitionToEnd不会创建无限循环吗? - Jaz
没有无限循环,因为它还改变了转换以使用“two”作为起始状态和“three”作为新的结束状态。上面的代码片段缺少两个之间的转换,MotionLayout 有一种处理方式,但更明确会更好。 - Jason Pearson

2
我遇到了类似的问题,其中我定义了2个转换:A -> B(称为转换X)和B -> C(称为转换Y),两者都在点击动画视图时触发。
在我的情况下,动作场景文件中转换的顺序很重要。无论哪个转换先被定义,都会先执行。因此,如果转换Y在转换X之上定义,则只有B -> C -> B转换是可能的。然而,将转换X放在转换Y之上后,以下转换就成为了可能:A -> B -> C -> B。
注意:A -> B仍然只能执行一次。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接