Search code examples
androidandroid-animationandroid-constraintlayout

How to animate the nested view on constraint layout?


I have a requirement where the view needs to be at the center of bottom and adjust the height based on its content. So created constraint layout inside root constraint layout to acheive this. But now I am facing a problem with animation. I am not able to apply 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>
  • If I need to go with constraintSet how to restructure my layout (without nested groups)?
  • Is there any other way we can achieve it? I feel like constraintSet is much straight forward to use.

Tried calling this animation constraint set which will pass nested constraint as param error_layout. But no changes on the result:

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

Layout:

enter image description here


Solution

  • Remember, ConstraintSet Animation works as any other animation with two stages - Animation Start and Animation End. So, First set the view constraint as Animation start which means hidden under bottom of the layout.

    So, Change the initial constraints as:

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

    Then, create the constraints as

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

    Then, if you want to hide it again. Just replace the TOP and BOTTOM as

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

    enter image description here

    GIF took more time than coding :p.

    EDIT - To listen to animation updates, especially Animation end as requested, what you can do is you can set a listener on 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) {
        }
    })
    

    Put it just before the beginDelayedTransition().