jeremie-martinez.com/2016/09/15/…

Last week, we renamed The Captain Train app to Trainline. This meant we had to change the colors, ICONS, white space and animations to match our new logo.

We had some problems creating new loading animations. So I thought this blog might be useful for fellow developers. In fact, I’ll try to explain how we designed and implemented this animation.

Full disclosure: It’s all device rendered, no video, no GIFs, just regular Views and vector animations.

background

Before the app was renamed, the animation we showed when loading search results looked like this:

It was very simple and we did it in less than three hours before the app was released. In fact, we only use animation-list drawable consisting of three drawable.

For Trainline, we want better results. Our designer created a beautiful design. Our challenge is to make it happen. Here’s what we ended up with:

Now let’s see how we created it.

implementation

There are several techniques for animation on Android. The first thing to note is that I want this animation to run on Ice Cream Sandwich (4.0), our minSdk.

Let’s look at a few possible ways:

Use GIF. This can be a great approach in Web development, but Android doesn’t really support GIFs. I don’t want to introduce a new library for this purpose. Also, I had to deal with different resolutions, which was a burden on our APK.

The use of video. I don’t want to use MediaPlay or anything like that. The reasons are resolution and weighted APK.

Use AnimatedVectorDrawable. Given the recent backward compatibility, this approach is worth considering. It’s great, but it brings in a lot of XML files. This process is also difficult to communicate between designers and developers, as it is difficult for designers to create AnimatedVectorDrawable. And I’m not sure it will run smoothly on low-end devices.

Use view animation. It’s easy for designers and developers, just the standard VectorDrawable. View animation is well handled by the Android Framework, optimized for overdrawing and performance.

design

The farther the slower

The first step is to design the inspection animation to give a sense of speed and movement. I need to break the original image into several layers and display them in ImageView:



Then, the rule is simple: “The farther, the slower.” For example, the wind obviously moves faster than the horizon.

The rules for a layer animation are also simple, each layer is the same: move the layer from right to left, starting on one side of the window and ending on the other. The repetition mode is an infinite loop. The farther the layer, the longer the duration of the animation. Let’s take a look at some code examples:

int translationXStart = totalViewWidth - mLayerView.getLeft() + OFFSET;
int translationXEnd = -mLayerView.getLeft() - mLayerView.getWidth() - OFFSET;

mLayerAnimator = ObjectAnimator.ofFloat(mLayerView,
                                        View.TRANSLATION_X,
                                        translationXStart,
                                        translationXEnd);

mLayerAnimator.setRepeatCount(ObjectAnimator.INFINITE);
mLayerAnimator.setRepeatMode(ObjectAnimator.RESTART);
mLayerAnimator.setInterpolator(LINEAR_INTERPOLATOR);
mLayerAnimator.setDuration(ANIMATION_DURATION);Copy the code

As you can see, this is very simple, this example is only animated on X, but some layers (trains and birds) may require animation on Y.

Another important detail is that to reduce memory footprint, each layer wraps only its contents, so Android only draws the smallest part.

You can see in the animation that the rails are the only layer that doesn’t move.

Currently we do not support RTL, and if we do, it will be easy to change the direction of animation.

Magic disappearance

Since the animation is looped infinitely, I must have a gradual fade-in effect as the layer reaches the sides. Once the bird reaches the left edge, it must enter from the right edge at the same time. I used a trick here: I used two translucent drawables as backgrounds. The principle is shown as follows:



The train moves naturally

For the train implementation I started by moving 1DP up and down. When I spoke to our designer, he suggested that I try to achieve a more natural effect than this. That’s not how trains vibrate, after all.

Interpolator I want to create a interpolator that follows my custom path:



The best approach is to use PathInterpolator. This interpolator is probably the least well-known among developers, but I find it much more powerful, especially now that it is possible to use PathInterpolatorCompat for forward compatibility.

Let’s look at the code:

private void prepareLocomotiveAnimation() { // First, create your path final Path path = new Path(); Path. The lineTo (0.25 0.25 f, f); Path. The lineTo (0.5 f to 0.25 f); Path. The lineTo (0.5 0.7 f, f); Path. The lineTo (0.9 f to 0.75 f); path.lineTo(1f, 1f); // Create the ObjectAnimatpr mLocomotiveAnimator = ObjectAnimator.ofFloat(mLocomotiveView, View.TRANSLATION_Y, -1dp, 0);  mLocomotiveAnimator.setRepeatCount(ObjectAnimator.INFINITE); mLocomotiveAnimator.setRepeatMode(ObjectAnimator.REVERSE); mLocomotiveAnimator.setDuration(ANIMATION_DURATION_LOCOMOTIVE); // Use the PathInterpolatorCompat mLocomotiveAnimator.setInterpolator(PathInterpolatorCompat.create(path)); }Copy the code

There’s really no mystery. It should be noted that I should use quadTo instead of ineTo to improve the Interpolation, which will be more smooth. Also note that for the repetition pattern of the animation I used reverse to make it more coherent.

Let the birds fly

This is the icing on the cake. Let’s see how I did it.

I said AnimatedVectorDrawable is great, right? It happens to be the best solution to the problem. The first step is to import your VectorDrawable:


    
Copy the code

The value of pathData is the vector with the wings on.

Then, you need an animator to move the wings up and down. The key point is to have the same number of path data points.

Copy the code

ValueFrom is the vector with the wings up, valueTo is the vector with the wings down.



Finally, we use AnimatedVectorDrawable to connect a VectorDrawable to an animator:


    
Copy the code

As you can see, this is a basic example that demonstrates the power of AnimatedVectorDrawables. It did have quite a touch up effect on the animation.

conclusion

To conclude, I’d like to say that user feedback is important! You always need someone to check if your design is meaningful, beautiful, and natural.

For me, WITHOUT Enrico’s visual design, I would not have been able to complete it. Thanks to Cyril and Thibaut for their feedback and inspection.