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