This morning I watched a great video with Sean McQuillan from Google about Keyframe Animations using ConstraintLayout and ConstraintSet. As I have a task in one of my current project where I need to do a rather complicated animation, I decided to sit down and learn this properly. This is the result and hopefully it can help others as well.


The task I have is to provide a screen for a kiosk-style application. There are three different options and the user can tap each to get more information before confirming their choice. The screenshot below shows the general idea, but the icons and text has nothing to do with the actual project (I choose these as I just picked three material icons to use for this sample).

A starting screen providing three different choices for the user

When the user taps one of the icons, it will zoom in on that (shrinking and fading the other two) and display another text with an additional description, as shown below.

Layout for the left option selected by the user

Before learning about the keyframe animation feature with ConstraintLayout and ConstraintSet, I was not looking forward to do all these animations. However, it turns out this is really simple and fun to do!

Defining the new constraints

We start with making three new copies of our original layout. These will hold the new ConstraintSet for each transition. My layout folder now looks like this;

Four almost identical layout files

The _left, _right and _middle each define the constraints for when that option is selected by the user.

Implementing the transitions

Next, I implement a function that will transition to the new ConstraintSet. The OvershootInterpolator makes the transition looks a bit better than the standard linear one. You can change this to whatever you feel looks best in your case.

fun updateConstraints(@LayoutRes id: Int) {
  val newConstraintSet = ConstraintSet()
  newConstraintSet.clone(this, id)
  newConstraintSet.applyTo(root)
  val transition = ChangeBounds()
  transition.interpolator = OvershootInterpolator()
  TransitionManager.beginDelayedTransition(root, transition)
}

Finally, I add a click listener to each icon where I call this method with the new ConstraintSet.

left.setOnClickListener {
  if (selectedView != left) {
    updateConstraints(R.layout.activity_main_left)
    selectedView = left
  } else {
    toDefault()
  }
}
middle.setOnClickListener {
  if (selectedView != middle) {
    updateConstraints(R.layout.activity_main_middle)
    selectedView = middle
  } else {
    toDefault()
  }
}
right.setOnClickListener {
  if (selectedView != right) {
    updateConstraints(R.layout.activity_main_right)
    selectedView = right
  } else {
    toDefault()
  }
}

The toDefault() method is simply making the same call but using the default ConstraintSet. This will be called when the user taps an already selected icon, or when tapping on the background.

private fun toDefault() {
  if (selectedView != null) {
    updateConstraints(R.layout.activity_main)
    selectedView = null
  }
}

The result of this is a nice transition between the different options. Since this requires very small amount of code, you no longer have any excuse not to add nice animations and transitions to your ConstraintLayout views. :)

Final result

If you want to try it out yourself, I’ve uploaded the project to my GitHub account.

Update (2017–12–17): As I learned yesterday, animating ConstraintSet as described above will only animate attributes that relates to layout, not attributes like text sizes. Those you will have to animate yourself.

Animating text sizes: There currently (2017–12–16) seems to be a bug in the ConstraintLayout library where it doesn’t animate text sizes. I filed a bug about this myself and also found another related one, so hopefully it will be fixed soon.