Exploring Android property animations
As mentioned in an earlier Xaminar, newer Android versions pack a lot of goodness for animations.
One of the new API I mentioned in the talk was property animations which let you “animate” any property on your objects including the non-graphical ones.
The corresponding class to that API is ValueAnimator
(doc) and more precisely, in our case, its subclass ObjectAnimator
(doc). Both let you construct animation over a range of normal scalar types like float or int for any property.
However, their true power lies in the fact that they can animate properties of any object type with the right configuration (more on that in the next section).
In addition, an often overlooked fact is that animations time behavior is not set in stone. Indeed, each of the animation API on Android can optionally take an TimeInterpolator
(doc) which defines the time shape of the animations.
For instance, following are the respective time shapes of two of the default interpolators used in the framework, LinearInterpolator
and AccelerateDecelerateInterpolator
:
Creating your own time interpolator is also very easy. You simply have to inplement the ITimeInterpolator
interface which has only one method: GetInterpolation(float)
.
The argument passed on that method is the current step of the animation which is a value between 0 (the start of your animation) and 1.0 (its end).
You are generally supposed to return a value in that range too but adjacent values are also supported and are being translated to an overshoot or an undershoot effect.
Additionaly, the formula you are using to compute your final value can be as complex as you want as long as it doesn’t impede the rendering loop (remember, for animations to be buttery smooth you want to aim for 60 FPS).
For instance, imagining I needed a time interpolator to represent the fall of an object (a typical quadratic equation), I could do it like that:
Which corresponds to the following shape:
So why is this useful?
Although you can generally use the default interpolator (AccelerateDecelerateInterpolator
), if your animations are supposed to model a real-life phenomenon (like my example of an object falling) then you should strive to find the equation that faithfully reproduce the physical behavior your users expect.
Practical example
Let’s now see a practical example of the two features I mentioned about property animation: customized time interpolator and animating over non-scalar values.
For our experiment we are going to be using Google Maps v2 API and try to add a bit of spice to Marker
objects which, by default, can’t be animated with the existing API.
The idea is to create a dropping effect for the pin:
I use this effect in Moyeu to inform the user of the result of his location search. The iOS Map application also use a similar effect for similar goals.
Since the only propery available to animate the pin is his latitude/longitude coordinates on the map we will have to determine a pair of geopoint respectively for the starting and ending position of a vertical movement.
Since the ending position is assumed to be known (i.e. we have the coordinates where the pin should be), we simply have to compute the initial offset of the pin when dropped.
With the Google Maps API, you can do these kind of display/map conversions using the Projection
class available on your Map
object. For instance computing the starting geocoordinates for our pin will be done like this:
We now have two geocoordinates between which we want to animate the pin using its SetPosition(LatLng)
method.
The problem is that by default the animation subsystem has no idea what a LatLng
type is and how to create the intermediate values between our starting and ending points.
Fortunately, as I explained in the beginning, we can extend the animation framework to accept any type with a bit of help. This help is materialized by a subclass of the ITypeEvaluator
interface.
The implementation of this interface is very similar in spirit to ITimeInterpolator
with a single method to implement which gives you three parameters: the desired intermediate value (represented by a fraction value between 0.0f and 1.0f), the start object instance and the end object instance.
Your job as an implementer is to provide a linear interpolation based on fraction’s value between those two object states (the axiom being that fraction = 0.0f ⇔ result = startObject and fraction = 1.0f ⇔ result = endObject).
Here is the straightforward implementation of your evaluator for a LatLng
object:
The last piece is that we want the pin drop to feel as natural as possible. Instinctively, when you think about an object drop you can picture it accelerating until it reaches the ground and then gently bouncing until all is initial inertia is gone.
Now fortunately we don’t have to model ourselves this behavior because it’s already available in Android through the well-named BounceInterpolator
class.
You can give a look to the formula implementation if you are interested but for the sake of the explanation I think the shape is a better illustration of the result:
So putting those two pieces together with our ObjectAnimator
friend, we can now implement the solution very concisely like this: