如何在约束布局上为嵌套视图设置动画?

How to animate the nested view on constraint layout?

我有一个要求,视图需要位于底部的中心,并根据其内容调整高度。所以在 root constraint layout 中创建了 constraint layout 来实现这个。但现在我面临动画问题。我无法申请 constraintSet.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#BEE">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/error_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginEnd="12dp"
        android:layout_marginBottom="40dp"
        android:background="#FFF"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <Button
            android:id="@+id/error_try_again"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="12dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="12dp"
            android:layout_marginBottom="12dp"
            android:textStyle="bold"
            android:minWidth="180dp"
            android:text="Try again"
            android:textAlignment="center"
            android:textColor="#000"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/error_subtitle" />

        <TextView
            android:id="@+id/error_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="12dp"
            android:layout_marginTop="12dp"
            android:layout_marginEnd="12dp"
            android:textStyle="bold"
            android:text="We are unable to find the object"
            android:textAlignment="center"
            android:textColor="@color/black"
            android:textSize="21sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/error_subtitle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="12dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="12dp"
            android:text="Try to keep the camera steady"
            android:textAlignment="center"
            android:textColor="@color/black"
            android:textSize="12sp"
            android:visibility="visible"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/error_title" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

尝试调用此动画约束集,它将嵌套约束作为参数传递 error_layout。但结果没有变化:

    private void animateResult(ConstraintLayout constraint) {
        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(this, R.layout.frameout_layout);

        Transition transition = new ChangeBounds();
        transition.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
        transition.setDuration(1200);
        TransitionManager.beginDelayedTransition(constraint, transition);

        constraintSet.applyTo(constraint);
    }

布局:

请记住,ConstraintSet 动画与任何其他动画一样工作,有两个阶段 - 动画开始和动画结束。所以,首先将视图约束设置为动画开始,这意味着隐藏在布局底部。

因此,将初始约束更改为:

<!-- Here app:layout_constraintTop_toBottomOf will position the view under the layout/screen -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/error_layout"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="12dp"
    android:layout_marginEnd="12dp"
    android:layout_marginBottom="40dp"
    android:background="#fff"
    android:visibility="visible"
    app:layout_constraintTop_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent">

然后,将约束创建为

//rootLayout is the Id of the parent layout
val constraintSet = ConstraintSet()
constraintSet.clone(rootLayout)
constraintSet.clear(error_layout.id, ConstraintSet.TOP)
constraintSet.connect(error_layout.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
val transition: Transition = ChangeBounds()
transition.interpolator = AnticipateOvershootInterpolator(1.0f)
transition.duration = 1200
TransitionManager.beginDelayedTransition(rootLayout, transition)
constraintSet.applyTo(rootLayout)

然后,如果你想再隐藏它。只需将 TOP 和 BOTTOM 替换为

constraintSet.clear(error_layout.id, ConstraintSet.BOTTOM)
constraintSet.connect(error_layout.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)

GIF 比编码花费的时间更多:p.

EDIT - 要收听动画更新,尤其是按要求结束动画,您可以做的是在 transition 上设置一个监听器 as

transition.addListener(object : Transition.TransitionListener {
    override fun onTransitionStart(transition: Transition) {
    }
    override fun onTransitionEnd(transition: Transition) {
    }
    override fun onTransitionCancel(transition: Transition) {
    }
    override fun onTransitionPause(transition: Transition) {
    }
    override fun onTransitionResume(transition: Transition) {
    }
})

把它放在 beginDelayedTransition() 之前。

我认为最简单的方法是使用转换管理器。

像这样

 findViewById(R.id.button).setOnClickListener {
        TransitionManager.beginDelayedTransition(constraintLayout)
        val constraint = if (changed) constraintSet1 else constraintSet2
        constraint.applyTo(constraintLayout)

P.S。 - 哦,我看到有人曾经说过同样的话并且有更好的解释。