Android: bring life to your custom view
Working with customers is always challenging. Many of them are requesting a pretty complex UI for the application, so they can impress their users and keep them using it.
And that quite naturally, everyone wants to build an app that does not look like any other, having a unique custom look, animation and behaviours that will prevent user from leaving it or even switching to different apps with similar functionality.
If you are new in Android canvas drawing and want to learn more about custom views you should probably check out my previous story. Instead, in this story we will take a look into some technics that will allow you to animate views, make them more responsive and looks natural.
Let’s get started
Before drawing anything we need to understand a simple way, how does custom view animate. And the answer is — ValueAnimator.
In android this class provides a simple timing engine for running animations which calculate animated values and set them on target objects.
By drawing any shape in android, of course you have to deal with lot’s of numbers. And basically what’s ValueAnimator does is providing you a set of numbers that are constantly changing, so using these in drawing will create a smooth animation for you.
Let’s take a look on it and implement a simple value animation, say, from 0 to 100. Here is how it is going to look like:
As you can see from example above we have a duration set to 2 seconds, that means that during this period of time value will be smoothly growing from 0 to 100.
Now it is time to draw our first animated shape. Let’s say we need to draw a rectangle that smoothes morphs to circle. To do that we will precede with regular round rectangle drawing, but instead of hardcoding rectangle corner radius we will animate it from 0 to (rectangle width / 2). So our onDraw method will look like this:
The next step is very similar to what we done above with ValueAnimator — all you need is to update radius variable.
But here is one more thing: all the drawing is frame by frame process, what means that if we want to animate our shape we will need to redraw it as new animated value comes out.
That’s why it is required to call invalidate() to make view redraw with new animated value.
On the gif file, you can see how it is going to look like.
That was it, a single animated property makes our shape transforming smoothly from square to circle! But what if we need to draw more complex shapes and animate multiple properties at once? Lets take a look on it without getting far from already made animation.
What we gonna add is a rotation. So to make a smooth morphing from square to circle with shape rotating around itself, we will need to animate at least two values at the same time, and ValueAnimator does support this out of the box.
By setting PropertyValuesHolder as values to ValueAnimator we can animate as much values at the same time as we need, so this is pretty handy in more complex animations, that you will probably use it your further projects.
By adding canvas.rotate(…) method just before drawing a rectangle, you should be able to achieve something like this.
canvas.rotate(rotate, viewWidth, viewHeight);
The time interpolator is used in calculating the elapsed fraction of this animation. The interpolator determines whether the animation runs with linear or non-linear motion, such as acceleration and deceleration.
In material design Google recommends to avoid linear animations, instead to use natural easing curves. Acceleration and deceleration changes should be smooth across the duration of an animation so that movement doesn’t appear mechanical.
Google took care of us and included various number of different interpolators in Android, so it should be enough for most cases.
Note: the default value is AccelerateDecelerateInterpolator
Let’s get a bit further on and try to create live analytics chart, that potentially could be used in many projects. This is quite common situation when you receive such gif file from your designer to implement something similar.
If you are new in Android, don’t be scary of it — it’s not as complicated as it may seem at a first glance.
We will try to analyse it and catch every part of it, so we can clearly understand what does it consist of and what exactly we have to animate. Moreover this is completely open source project, so you can look more closely on it if you want.
You can also find many of this kind charts on GitHub as cool, customisable libraries, that are quite popular between developers, but as we are only learning this technics, we will try to make our own one!
Take a closer look
While developing a custom view, it is important to separate the whole view into some pieces to simplify it and understand what exactly we need to draw, so let’s take a closer look at this analytics chart.
As you can see, chart is made by two main parts: background, and chart lines on foreground. Background is made by static lines and text drawing, so there is nothing special here.
What is more interesting — is animated chart lines. The only difference here from above examples, is that we have to deal with multiple lines, that are animated by sequence. First of all let’s take a look on single line and define what values are animated.
In Android, line drawing is made by specifying start (X; Y) and stop (X; Y) coordinates. In our example we are animating only stop (X, Y) and start coordinates stays still. Also, after each line (except last one) there are a little circle on the end, which is displaying with alpha animation, so that is another one value we need to animate.
If summed up, to draw a single animated line with circle on the end we need to animate 3 values at the same time: line stop X, Y coordinates, and color alpha.
Note: alpha on canvas has value [0.. 255]
Now we need to deal with multiple chart lines. As there several of them, we need ValueAnimator for each of them with specific properties to animate, so we can create a collection of ValueAnimators to do that.
Now we need to play a list of ValueAnimators in sequence so we can receive multiple values from multiple ValueAnimators, to do that we will use AnimatorSet.
This class plays a set of Animator objects in the specified order. Animations can be set up to play together, in sequence, or after a specified delay. With AnimatorSet you can control your Animators and play them in any order you like, so it perfectly fits in our task.
Note: AnimatorSet will perform during a total duration of Animators it contains.
So playing around with AnimatorSet will let you to receive values for each line of chart by sequence.
One more important note here: by playing AnimatorSet you will receive values only for lines that are currently animating. But after each invalidate() call you will also need to redraw chart lines that are done with it’s animation.
Don’t forget to check out complete project of ChartView to discover how it is drawn and all the animation in it. Hopefully this story will help you to understand the canvas animation process on Android and build your own custom view without any issues.