Android ConstraintLayout Animation:Part I

In this tutorial, we will talk about how to implement ConstraintLayout Animations in our Android Application using a simple example.

Get GITHUB code from here.

ConstraintLayout Animations

Animations make your app look polished, improve engagement, and are fun to build. With ConstraintLayout it’s easy to build complex animations involving several views at once and to do so, we need to just change constraints!

By specifying your animations via keyframes, it is easy to fluidly animate complex scenes without a lot of code.

Within a ConstraintLayout, you can animate changes to the size and position of elements by using ConstraintSet and TransitionManager.

The most common and easy way to build an animation using ConstraintSets, specify two layout files which act as a start and end keyframe for the animation. You can then load a ConstraintSet from the second keyframe file and apply it to the displayed ConstraintLayout.

Note: ConstraintLayout and ConstraintSet supports from API 9 onwards and  TransitionManager is available from API level 14 onwards.

ConstraintSet

ConstraintSet is a class that allows you to define programmatically a set of constraints to be used with ConstraintLayout.

In other words, we can say ConstraintSet is a lightweight object that represents the constraints, margins, and padding of all child elements within a ConstraintLayout.

It lets you create and save constraints, and apply them to an existing ConstraintLayout.

When you apply a ConstraintSet to a displayed ConstraintLayout, the layout updates the constraints of all of its children.

A ConstraintSet is created just like any other Java object:

ConstraintSet constraint = new ConstraintSet();

Important: ConstraintSet animations animate only the size and position of child elements. They do not animate other attributes (such as color).

ConstraintSet Methods

connect(): This method is used to set constraints on the views programmatically. It establishes connections between sibling views or a view and the parent view.

Both clone() and load() methods of ConstrainSet class do the operation similarly with a slight difference.

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.

load(): It will parse the XML and then absorb all the layout related constraint mappings of the views within the constraint layout.

To create a ConstraintLayout animation we need:

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.

A Simple Example

The goal of our example is the following animation — which we want to achieve.

constrain_image_anim

As you can see, we have our ic_launcher_round logo at the top of our screen. On tapping the image, it has to come to the bottom of the screen. Also, notice that when the image reaches the bottom of the screen, it’s size is increased.

Creating Our Layouts

Let’s create our first layout of the MainActivity which is activity_main.xml. Pay close attention to the constraints.

activity_main.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"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher_round" />

</androidx.constraintlayout.widget.ConstraintLayout>

Screenshot 2019-12-04 18.37.24

Let’s create our final layout or the second frame — activity_main_animation.xml. In this second layout, to make our image bigger, I’ve increased it to 250dp.

activity_main_animation.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:srcCompat="@mipmap/ic_launcher_round" />
</androidx.constraintlayout.widget.ConstraintLayout>

Screenshot 2019-12-04 18.37.57

 

Creating Animation

Now, we need to make this transition smooth by defining the animation, with the starting point as activity_main.xml and ending point as activity_main_animation.xml.

ConstraintLayout root;
ImageView imageView;

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

    root = findViewById(R.id.root);

    imageView = findViewById(R.id.imageView);

}

private void showAnimation() {
    show = true;

    ConstraintSet constraint1 = new ConstraintSet();

    constraint1.clone(this, R.layout.activity_main_animation);


    TransitionManager.beginDelayedTransition(root);
    constraint1.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.constraintanimationdemo;

import android.os.Build;
import android.os.Bundle;
import android.transition.TransitionManager;
import android.view.View;
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 imageView;
    boolean show = false;

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

        root = findViewById(R.id.root);

        imageView = findViewById(R.id.imageView);

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (show)
                        revertAnimation();
                    else
                        showAnimation();
                }
            }
        });

    }

    private void showAnimation() {
        show = true;

        ConstraintSet constraint1 = new ConstraintSet();
        constraint1.clone(this, R.layout.activity_main_animation);


        TransitionManager.beginDelayedTransition(root);
        constraint1.applyTo(root);
    }

    private void revertAnimation() {
        show = false;

        ConstraintSet constraint2 = new ConstraintSet();
        constraint2.clone(this, R.layout.activity_main);

        TransitionManager.beginDelayedTransition(root);
        constraint2.applyTo(root);

    }

}

Exploring MainActivity.Java

  • We created 2 ConstraintSet() — constraint1 and constraint2.
  • We got the constraints from activity_main.xml using clone() and mapped it to constraint1.
  • We got the constraints from activity_main_animation.xml using clone() and mapped it to constraint2.
  • Then with the help of our TansitionManager, we initiated a default transition using beginDelayedTransition() to our root ConstraintLayout.
  • Based on the set value, we changed and applied the Constraints using the method applyTo().

Now, that’s all! no more additional steps. Run the app and check it yourself. Tap the image and you will see the keyframe animation in place.

Duplicating XML layout?

As you can see, we need to duplicate the layouts to achieve the animation or transition. But nobody would like to duplicate anything. But when you clone the constraints, it takes only the layout related constraints and attributes such as layout width and height. But not the styling attributes — such as textSize, color, etc.. So you need not replicate the exact styling in all the alternate layouts.

You can achieve the same result using transition framework!

You are right in that none of this is new. You can achieve the same result with the transition framework. However, this becomes powerful when the animation you are trying to implement can be nicely specified using specific constraints (e.g. chained elements, guidelines, etc.) that would otherwise take a lot of work to achieve.

ConstraintLayout will only perform animation on its direct children since it only knows when you change layout parameters and constraints on the children that it handles. It means that it won’t handle nested ViewGroups very nicely. But nested ConstraintLayout may solve this problem.

I hope this article will help you in understanding the basics of how to implement  ConstraintLayout animations in android applications. Next, we’ll see another use-case and we’ll also learn how to implement other transition effects.

 

 

 

Leave a Reply