Can you use MotionLayout to create dynamic animations of data? That’s any data you don’t know about at compile time — like user input. Yes! Absolutely. You can use the MotionLayout code API to create a MotionScene dynamically in code.
In this article we will be using developing a dynamic histogram that animates its changes using MotionLayout. How one would build a histogram view widget that can take any arbitrary data and able to display, sort using MotionLayout:
This example is designed to have highly dynamic data to explore how to implement animations in code using MotionLayout.
Let’s dive in!
The source code for the example below can be found here:
Before we get started
The article assumes a basic knowledge of MotionLayout, ConstraintLayout, ConstraintSet and Android UI in general. If you’re new to MotionLayout, check out the MotionLayout codelab to learn the basics of building animations using MotionScene, Transition, Constraints, and ConstraintSets.
There are few articles about MotionLayout providing great overview of the system. We highly encourage you to start with them before reading this article:
- Introduction to MotionLayout (part I)
- Custom attributes, image transitions, keyframes (part II)
- Taking advantage of MotionLayout in your existing layouts (CoordinatorLayout, DrawerLayout, ViewPager) (part III)
It is recommended to go through at least part I of the articles mentioned above.
We’ll start off with a custom ConstraintLayout that can create and set histogram bar UI programmatically. We’ll use NonAnimatingHistogramWidget.kt as our backbone.
With this widget we can display any arbitrary data but it’s quite boring to look at:
This Widget is capable of displaying a dynamic histogram using ConstraintLayout. You should take a moment to explore how it works.
- In onFinishInflate the bars are created and added by calling createBars. This instantiates mSize buttons, adds them to the View, and creates a horizontal chain to lay them out.
- When the data changes, setData is called. This is responsible for updating the constraints to match the new data. Remember, we’re not animating, so there’s only one ConstraintSet (the endSet or desired screen). With the new heights applied to this, ConstraintLayout immediately jumps the bars to the new data.
Step 1. Creating a placeholder transition
First we need to convert from ConstraintLayout to a MotionLayout. MotionLayout does require that we have a MotionScene in order to run so we’ll create one in code. The placeholder transition will go from the current layout to the current layout — this isn’t a very exciting animation yet but it’ll give us the code we need to build more animations in a second..
We can create this transition + motion scene programmatically:
Let’s go through this step by step.
- After calling createBars like before, which sets up the default ConstraintSet containing a horizontal chain, we’re ready to start creating a transition.
- Then we create the start and end constraint set, and make it the same as the current layout. You might have noticed ConstraintSet ids we’re creating (e.g. startSetId and endSetId). These ids are what MotionScene understands.
- Then creation of the placeholder transition. Here, Transition also gets an ID.
- Now that we have a Transition, we can create a MotionScene, apply the transition, and then set it as the MotionScene for this MotionLayout.
Every MotionLayout requires a MotionScene and Transition, so you’ll need to apply similar code to any custom MotionLayout classes you build.
MotionLayout requires that all ConstraintSet and Transition instances have a unique ID.
View.generateViewId is a convenient way to generate these on the fly in your app.
If your app cannot use View.generateViewId due to API version restriction, you can always just create an empty motion scene.xml and set it as a
app:layoutdescription like below:
And in HistogramWidget.kt:
This is the same as creating the placeholder transition programmatically.
In this step we’ll learn how to use our placeholder transition.
Let’s look at the changes we’ll need to make in setData:
Unlike Non Animating HistogramWidget, Animating HistogramWidget requires 2 ConstraintSets, the current (
startSet) state and the next (
endSet) state. Luckily, we just created two ConstraintSets in our Transition. As we go through the data, we can create a transition by setting a different height on the start and end ConstraintSet.
This is how it’d look like:
This is pretty straight forward. You’re setting transition from the start to the end, and afterwards, we’re cloning the end state to the start. This might not be necessary as we re-enforce the currentHeight from setData, but It’ll come in handy later in the example.
With just these changes, you should be able to see the setData working correctly:
We’ve briefly looked into how to animate when the end state is receiving dynamic data. Can we do the same for the start state?
In the setData method, we are resetting the startSet with currentBars which is based on the results of the previous animation. If we were to replace this with the current height of the bars, which is dynamic, this will allow animation to be interruptible. The new animation can start before the previous animation is not yet finished! Basically, the start state can also be dynamically adjusted in code.
Here’s the example on how to make setData interruptible:
We’ll not cover further on interruptible animation to keep things short but we encourage you to see the examples from “setData”, and implement your own “animation-interruptible sort”!
This section we’ll implement sort and learn the best practice on dynamic data animation.
For starters, here’s an example on sort implementation:
Similar to setData, we’re basically updating the start and the end constraint set. When sorted, we’ll rely on the mNextBars to determine the chain id orders.
Animate widget code doesn’t need to change at all.
See the github code to see what else is changed from NonAnimatingHistogramWidget.kt. With few more tweaks on data, you should be able to see the sorting working:
There you have it! To work with dynamic data,
- We’ve created a placeholder transition, with the start ConstraintSet and the end ConstraintSet that clones the layout.
- We updated the start and the end ConstraintSet accordingly to the animation we wish to show
- We triggered the animation from the start to end
MotionLayout lets you build rich animations, and it’s quite fun to use Motion Editor and XML to build beautiful animations. When you poke behind the covers, it turns out you can build complex MotionLayout animations entirely in code!
I can’t wait to see what you build.