Android ConstraintLayout Animation:Part II

In the previous article, Android ConstraintLayout Animation: Part I we have talked about how to implement keyframe animation with a simple example using ConstraintLayout and ConstraintSet.We also peeked into few methods which made our animation possible.

Now In this article, we’ll see another use-case and we’ll also learn how to implement other transition effects.

Let’s see first what we’re going to build: Our goal is to create an animation that shows-off the Hampi detail when the user presses the screen, with seamless and fluid animation.

Get GITHUB code from here.

 

This animation is created with two different layout files: hampi.xml and hampi_detail.xml.

Creating Our Layouts

Let’s create our first layout of the MainActivity which is hampi.xml. In this layout, the views are the same as the second layout, but they are hidden outside the layout.

Screenshot 2019-12-05 19.47.14

hampi.xml 

<?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:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#181818"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/backgroundImage"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/hampi_img"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#d3d3d3"
        android:paddingStart="16dp"
        android:paddingTop="3dp"
        android:paddingEnd="16dp"
        android:paddingBottom="3dp"
        android:text="Ballari district, Karnataka"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@+id/title"
        app:layout_constraintRight_toRightOf="@+id/title" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:background="#673AB7"
        android:paddingStart="24dp"
        android:paddingTop="8dp"
        android:paddingEnd="24dp"
        android:paddingBottom="8dp"
        android:text="Hampi"
        android:textColor="#FFFF"
        android:textSize="45sp"
        app:layout_constraintRight_toLeftOf="@+id/backgroundImage"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/fadeView"
        android:layout_width="wrap_content"
        android:layout_height="90dp"
        android:foreground="@drawable/gradient"
        app:layout_constraintBottom_toTopOf="@+id/description"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="12dp"
        android:text="Tap for info"
        android:textColor="#ffffff"
        android:textSize="15sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#181818"
        android:gravity="center"
        android:paddingStart="8dp"
        android:paddingEnd="8dp"
        android:paddingBottom="8dp"
        android:text="@string/hampi_detail"
        android:textColor="#FFFF"
        android:textSize="22sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/backgroundImage" />


</androidx.constraintlayout.widget.ConstraintLayout>

Let’s create our final layout or the second frame — hampi_datail.xml. This layout consists of the final views position.

Screenshot 2019-12-05 19.48.15

hampi_datail.xml

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/backgroundImage"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/hampi_img"
        app:layout_constraintBottom_toTopOf="@+id/description"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:background="#673AB7"
        android:paddingStart="24dp"
        android:paddingTop="8dp"
        android:paddingEnd="24dp"
        android:paddingBottom="8dp"
        android:text="Hampi"
        android:textColor="#FFFF"
        android:textSize="45sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#d3d3d3"
        android:paddingStart="16dp"
        android:paddingTop="3dp"
        android:paddingEnd="16dp"
        android:paddingBottom="3dp"
        android:text="Ballari district, Karnataka"
        android:textSize="12sp"
        app:layout_constraintRight_toRightOf="@+id/title"
        app:layout_constraintTop_toBottomOf="@+id/title" />

    <View
        android:id="@+id/fadeView"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:foreground="@drawable/gradient"
        app:layout_constraintBottom_toTopOf="@+id/description"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:text="Tap for info"
        android:textColor="#ffffff"
        android:textSize="15sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/description"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#181818"
        android:gravity="center"
        android:paddingStart="8dp"
        android:paddingEnd="8dp"
        android:paddingBottom="8dp"
        android:text="@string/hampi_detail"
        android:textColor="#FFFF"
        android:textSize="22sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

As you can see, the layouts are exactly the same, the only difference is that in the first one (hampi.xml) the elements are placed offscreen, while in the second one they are in the desired position.

Creating Animation

So, let’s build the animation and let’s see how is incredibly easy to make animations with ConstraintLayout and ConstraintSet.

First, we need to create a new instance of ConstraintSet, then we can clone the constraints of the second layout (hampi_detail.xml) by calling the clone() method.

ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(this, R.layout.hampi_detail);

clone()It will inflate the layout and absorb all the layout related constraint mappings (layout_width,layout_height,layout_marginTop etc. and not styling ie. textSize, color etc) of the particular views within the constraint layout.

Let’s add some transition effects.

Now, let’s create the transition object used to set the interpolator and the duration of animation:

ChangeBounds transition = new ChangeBounds();
transition.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
transition.setDuration(1200);

ChangeBounds() captures the layout bounds of target views before and after the scene change and animates those changes during the transition.

Then we define the type of interpolator — which defines the rate of change of an animation. This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated, decelerated, repeated, etc. Here’s it’s AnticipateOverShootInterpolator.

And, last but not least, we need to call the TransitionManager.beginDelayedTransition() used to create a new scene and to run the transition on the next rendering frame. Lastly, we call applyTo() to finally start the animation.

TransitionManager.beginDelayedTransition(root, transition);

//here root is the name of view to which we are applying the constraintSet
constraintSet.applyTo(root);

TransitionManager — This class manages the set of transitions that fire when there is a change of Scene. Setting specific transitions for scene changes is not required; by default, a Scene change will use AutoTransition to do something reasonable for most situations. Specifying other transitions for particular scene changes is only necessary if the application wants different transition behavior in these situations.

beginDelayedTransition() —  Convenience method to animate, using the default transition, to start the transition from the first scene to the second scene(first layout to the second layout).Equivalent to calling beginDelayedTransition(ViewGroup, Transition) with a value of null for the transition parameter.

applyTo() — sets or applies the new or requested constraints to the view specified.

Below you can see you have to write only these few lines of code to achieve this animation.

private void showComponents() {

    ConstraintSet constraintSet = new ConstraintSet();
    constraintSet.clone(this, R.layout.hampi_detail);

    ChangeBounds transition = new ChangeBounds();
    transition.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
    transition.setDuration(1200);

    TransitionManager.beginDelayedTransition(root, transition);

    //here root is the name of view to which we are applying the constraintSet
    constraintSet.applyTo(root);
}

In our MainActivity.Java, let’s create the click listener for our image and add the following code.

MainActivity.Java

package com.example.androidconstraintdemo;

import android.os.Bundle;
import android.transition.ChangeBounds;
import android.transition.TransitionManager;
import android.view.View;
import android.view.animation.AnticipateOvershootInterpolator;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;

public class MainActivity extends AppCompatActivity {

    ConstraintLayout root;
    ImageView backgroundImage;
    boolean show = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.hampi);

        root = findViewById(R.id.root);
        backgroundImage = findViewById(R.id.backgroundImage);

        backgroundImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (show)
                    // if the animation is shown, we hide back the views
                    hideComponents(); 
                else
                    // if the animation is NOT shown, we animate the views
                    showComponents();
            }
        });
    }


    private void showComponents() {

        show = true;
        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(this, R.layout.hampi_detail);

        ChangeBounds transition = new ChangeBounds();
        transition.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
        transition.setDuration(1200);

        TransitionManager.beginDelayedTransition(root, transition);

        //here root is the name of view to which we are applying the constraintSet
        constraintSet.applyTo(root);
    }

    private void hideComponents() {
        show = false;

        ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(this, R.layout.hampi);

        ChangeBounds transition = new ChangeBounds();
        transition.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
        transition.setDuration(1200);

        TransitionManager.beginDelayedTransition(root, transition);

        constraintSet.applyTo(root);
    }


}

I hope this article will help you in understanding another use-case of how to implement ConstraintLayout animations with transition effect in android applications.

Leave a Reply