Randomized, endless animation using TimeAnimator

Patrick Elmquist
Aug 7, 2017 · 7 min read

Learn how to create an endless, randomized animation.

What we are doing…but a bit more endless and a little less looping!

Introduction

This is my second post on the subject of creating frame by frame animations. The first post covered the basic of creating an endless, looping animation using a ValueAnimator and can be found here:

What if you want an animation that is endless but not looping, where new items are added randomly, keeping the animation interesting? The last post relied on having a start and an end value to interpolate between and does not really support a case with only a start value. The main principle for a continuous animation is however still the same, we need a loop that updates the state and tells the system to draw. What we need is another type of Animator and also, since we’re going to have a lot more items moving, a better way of keeping track of the item state. To achieve this we’ll be using a simple model class for the state and a TimeAnimator for an endless loop.

The demo project used in this tutorial can be found here:

The demo project can be downloaded as an APK here.

The state

To keep track of each stars state between frames we’ll be using an array of Star objects:

It contains all information needed to progress a star from one frame to the next. What we’ll see later is that once the array is populated with Star objects, we’ll reuse those objects in favor of allocating a new object for each new star entering the screen.

With the state declared, lets write a method to randomize a star. There are several ways of creating a method like this. You could go with a semi-random version where you choose a random position in a predefined array of values, or like in this case, randomize everything.

  • X A point in the range [0, width]
  • Y Since we want the star to enter from the bottom of the view, we start of by adding the view height. If left like that, half the star would be visible at the start of the animation. This is because the star is drawn with X,Y as center coordinates. To counter this, we add the star size to the Y coordinate. Now the star is completely outside of the view. To further randomize the position we add a random offset, which function as a delay for when the star will enter the screen again. The bigger offset, the longer delay. Y = VIEW_HEIGHT + STAR_HEIGHT + RANDOM_OFFSET
  • Scale The scale is defined in two parts, the SCALE_MIN_PART and SCALE_RANDOM_PART. It’s a way to control the randomization a bit by adding a min scale while still allowing stars to have different sizes.
  • Alpha The alpha is also defined in two parts, one controlled by the scale and one randomized. This is a way to let bigger stars be brighter which makes them feel like they’re in the foreground compared to smaller, less visible stars.
  • Speed The speed is defined by the scale and the alpha, the bigger and brighter a star is, the faster it moves.

So when is a good time to initialize the array? The X,Y coordinates are going to be scattered around the view and in order to do that we need the view to be measured so we know the width and height. onSizeChanged(...) is a good place to do this, at that point we have a width and height to work with and also if the view size is changed, the items will be re-randomized.

TimeAnimator vs ValueAnimator

My last post on the subject covered how to make a frame by frame loader animation (available here) which used a ValueAnimator to progress the animation. So why not just do the same in this tutorial? Well let’s check how ValueAnimator differs from TimeAnimator:

ValueAnimator is a good choice when your animation has a defined start and end, for example when moving a View from Point A to B, or when creating an endless but repeating animation, like a progress loader, where the same animation loop over and over. The value provided by the ValueAnimator update callback is an interpolated value between the start and end values.

// Example
int from = Color.WHITE;
int to = Color.BLUE;
ValueAnimator animator = ValueAnimator.ofArgb(from, to);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final int currentColor = (int)animation.getAnimatedValue();
}
});

TimeAnimator only has a defined start and is good if you have a continuous animation without a defined end or reset point. Since there’s no end, the animator cannot provide an interpolated value to use to progress the animation, instead the TimeAnimator provides time deltas. When it’s time to update the state, both the total duration so far and the elapsed time since the last update, is provided.

// Example
TimeAnimator animator = new TimeAnimator();
animator.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator a, long total, long dt){
// total = millis since animation started
// dt = millis since last update
}
});

In this tutorial we are creating an endless animation with a lot of random parameters, hence a TimeAnimator is the most suitable choice.

The loop

With the type of Animator decided we have a loop, time to make it do something:

Progress the animation by updating the state of all items, based on the elapsed time. The conversion from millis to seconds is mainly to have more understandable constants. When tweaking values, it’s a lot easier doing so in the context of 600px/s rather than 0.6px/ms.

The update itself is pretty straight forward, we move the Y coordinate of the star with speed * deltaSeconds pixels. Then we check if the star has moved off screen, and in that case we initialize it again, moving it back to the bottom of the view and re-randomizing its attributes. This is all the code needed to recycle the finite number of Star objects that we already have. This is preferable to allocating new objects for each new star since we’ll use less memory and put less stress on the garbage collector.

The reason we check if star.y + size < 0 is that the y coordinate points to the center of the star. So if we’d only check if star.y < 0, the star would be recycled (and disappear) while half the star is still visible to the user.

Calling invalidate() tells the system that we want onDraw() to be called so we can make the new state visible.

All there is left is to draw the state to the screen. To get the stars into position and rotating them, we’ll be modifying the matrix inside the canvas, using the translate and rotate methods. What’s important to remember is that when you’re finished drawing a star, you must restore the state of the canvas matrix before proceeding with the next star. That’s done using the Canvas#save() and Canvas#restoreToCount(int) methods, as seen below. Of course you can draw the state to the canvas without manipulating the canvas, this is just the way I prefer to do it.

The rest of the code is pretty straight forward, we calculate the size of the star, resize the drawable and draw it to the canvas.

Note: Performance

One thing to have in mind when doing these kinds of animations is the performance. How heavy the animation is on the device will be determined by the time it takes to update the state and draw it. Keep this in mind when developing and keep the number of items at a relatively low count and you should be fine. Also, if you are targeting older devices it might be a good idea to categories devices and determine a suitable item count based on the hardware. I won’t go into further detail on how to categories devices, however you could make assumptions based on the screen resolution/density or use the Device Year Class library from Facebook, available here.

Note: Activity life cycle

When doing a quick animation, like fading a view, it’s not a big deal if it continues running when the app is put in the background (user pressing the Home or Recent button) since it stops after ~300ms. In the case of a continuous animation however, it’s a good idea to follow the Activity life cycle more closely to avoid having the Animator running when nothing is being shown. So lets define pause/resume methods to be called from the parent Activity.

Have the Activity call these methods from onPause() and onResume() and the TimeAnimator will pause/resume accordingly.

Wrapping up

That’s all there is to it, this is just a simple example of what you can do by with this kind of animation. The possibilities are as endless as the animation ;)

Don’t forget that the source for the demo project is available at my Github account, here.

Patrick Elmquist

Written by

Android/iOS Developer@Knowit Mobile. Caffeinated unicorn of excellence, with an interest in UX and design

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade