In the previous post on property animations we have come to know two types of animators. The ObjectAnimator
animates a property of a single object. The AnimatorSet
is a container that manages multiple Animator
objects and has the functionality to choreograph complex animations. The functionality of the two classes is very general and should be sufficient in most scenarios. In some rare cases, however, the two classes are not sufficient and you need some more low-level access to the animation data. This is the use case of the TimeAnimator
. The TimeAnimator
was added in API 16 and provides a low level interface to the property animation system.
Imagine that you want to animate a property of an object that does not have a setter method. Then you could either modify the object and add a setter method for the animated property. But sometimes this is not possible because the object is defined by some external library. Another solution would be to write a wrapper class that holds an instance of the object to be animated and provides a setter method that allows you to use the ObjectAnimator
. Another solution would be to use the TimeAnimator
. The time animator is particularly useful if you don’t need to worry about interpolators but instead want to know explicitly the elapsed time in milliseconds.
The TimeAnimator
doesn’t actually animate anything itself. It relies on a TimeListener
to do the work and is only responsible for calling the TimeListener
every time the animation is updated. TimeListener
is an interface that follows the observer design pattern. TimeAnimator
defines only two public methods.
void setTimeListener(TimeAnimator.TimeListener listener) void start()
The method setTimeListener
is used to register the time listener with the animation. In contrast to the listeners that we encountered in the previous tutorial on property animations, the time animator hold only a single time listener. Calling setTimeListener
will remove any previously registered listener from the animator. The start method behaved in the same way as for the other Animator classes. Calling it will start the animation. Note that the TimeAnimator
extends the Animator
class. This means that you can still call all of the methods defined in Animator
, such as setDuration
and setStartDelay
, or control the animation’s flow with start
, end
, cancel
, pause
and resume
, or add AnimatorListener
or AnimatorPauseListener
objects. However, the TimeAnimator
does not listen to its duration. It will keep on running until it is explicitly stopped by calling end or cancel.
If you want to make use of the TimeAnimator
then you have to implement your own time listener. The TimeListener
is a very simple interface defining only one method.
void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime)
When you implement a TimeListener
and register it with the TimeAnimator
then the TimeAnimator
will call the method onTimeUpdate
every time that the animation is updated. Three arguments are passed to the method. The first argument is the TimeAnimator
that issues the call. The object is passed to the TimeListener
because the listener could be registered with multiple animations. This way the listener knows which animation was updated. The second and third argument provide information about the animation time. totalTime
is the time in milliseconds that the animation has been running. The argument deltaTime
is the time in milliseconds that has elapsed since the last update of the animation.
Example: Showing Frames per Second
As always, it is best to understand the TimeAnimator
by example. In this tutorial we will develop a time animator that displays the frames per second (fps) information on the screen as another animation is running. We will first create an implementation of TimeListener
that updates a TextView
with the fps number. The class will contain a double
member that holds the fps value and a reference to the TextView
that should be updated.
class FpsTimeListener implements TimeListener { private double fps; private TextView textView; public FpsTimeAnimator(TextView textView) { this.textView = textView; this.fps = -1.0; } void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { // code follows below } }
The constructor takes the reference to the text view object and initializes the fps number with a negative value. In the calculation of fps
we will be using a running average to reduce fluctuations in the output. The negative value indicates that the value has not yet been calculated. The current fps
value is calculated simply from the deltaTime
value.
double currentFps = 1000.0/(double)deltaTime;
However, at the start of the animation deltaTime
can be zero. In this case the calculation above would result in infinity. We therefore have to modify the code like follows.
double currentFps; if (deltaTime != 0) currentFps = 1000.0 / (double) deltaTime; else currentFps = 0.9 * fps;
If this is the first evaluation of the frames per second, the fps
value is set directly. If, on the other hand, fps
already contains a positive number than a running average calculation is performed.
if (fps<0.0) fps = currentFps; else fps = 0.9*fps + 0.1*currentFps;
Finally, the text field has to be updated with the fps
value.
textView.setText(String.format("fps: %.2f",fps));
Next we create a simple animation. We use a similar layout to the animation in the previous tutorials. We have an ImageView
with id some_image
and we have a TextView
which we call fps_text
. In our activity we first create an animation that will rotate the image 5 times by 360 degrees. We do this in the onCreate
method of our activity.
protected void onCreate(Bundle savedInstanceState) { ImageView someImage = (ImageView) findViewById(R.id.some_image); ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(someImage, "rotation", 0, 360); rotateAnim.setDuration(1000); rotateAnim.setRepeatCount(5); rotateAnim.setRepeatMode(ObjectAnimator.RESTART);
Next we create aFpsTimeListener
that will update thefps_text
TextView
.
fpsText = (TextView) findViewById(R.id.fps_text); FpsTimeListener listener = new FpsTimeListener(fpsText);
This listener is added to a TimeAnimator
that we will declare final
for reasons that we will see later.
final TimeAnimator timeAnim = new TimeAnimator(); timeAnim.setTimeListener(listener);
Now we collect the two animations in an AnimatorSet
and instruct the set to play both animations together.
anim = new AnimatorSet(); anim.play(rotateAnim).with(timeAnim);
We are ready to start the animation. We do this in the callback method startAnimation
which is linked to the image’s onClick
event.
public void startAnimation(View view) { anim.start(); }
This will work nicely, but if you try it out you will notice that the TimeAnimator
will never stop. This is because it does not listen to its duration but, as explained above, needs to be stopped manually by calling the end
method. In order to achieve this we add the following code to the onCreate
method.
rotateAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { timeAnim.end(); } });
We only want to receive a callback when the animation ends. We use the convenience class AnimatorListenerAdapter
described in the previous tutorial and override the onAnimationEnd
method.
This will stop the timeAnim
animation when the rotateAnim
animation has ended. Note that the call to timeAnim.end
inside the anonymous class requires timeAnim
to be declared final
. This is the reason for declaring it final
above. The resulting animation can be seen on the right. In this case we see that, for this simple animation, we obtain a rate of around 60 frames per second. This is a good value and results in a nice smooth appearance of the animation.
This tutorial concludes the series on property animation. If you found the tutorials useful, please spread the word. You can also follow me on Google+, Facebook or Twitter.
Mikail

Looks great tutorial.I need one help,Currently I am working gridview calendar with listview.In the main Layout I have placed Gridview in top,Listview is placed under gridview.In the gridview I am displaying the current week only.When a user scroll down I am displaying the whole month and the listview is displayed under gridview.I need to animate the listview, when a user scroll down along with gridview i.e when a user scroll up the listview will move upward along with gridview with animation, when user scroll down listview will move downwards along with gridview with animation.Please help me to achieve this.
Regards,
Venkatesan.R
Hi,
I am trying to measure FPS of an Animation Set where animators in the animator set is animating property “left”. The code is below:
package com.example.fontproto;
import java.util.ArrayList;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity {
AnimatorSet set = new AnimatorSet();
private TextView fpsText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
ImageView someImage = (ImageView) findViewById(R.id.some_image);
someImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
set.start();
}
});
//ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(someImage, “rotation”, 0, 360);
ObjectAnimator leftAnim = ObjectAnimator.ofFloat(someImage, “left”, 0, 300);
// ObjectAnimator leftAnim2 = ObjectAnimator.ofInt(someImage, “top”, 0, 100);
ArrayList list = new ArrayList();
// rotateAnim.setDuration(1000);
// rotateAnim.setRepeatCount(5);
// rotateAnim.setRepeatMode(ObjectAnimator.RESTART);
leftAnim.setDuration(1000);
leftAnim.setRepeatCount(5);
leftAnim.setRepeatMode(ObjectAnimator.RESTART);
// leftAnim2.setDuration(1000);
// leftAnim2.setRepeatCount(5);
// leftAnim2.setRepeatMode(ObjectAnimator.RESTART);
fpsText = (TextView) findViewById(R.id.fps_text);
FpsTimeListener listener = new FpsTimeListener(fpsText);
final TimeAnimator timeAnim = new TimeAnimator();
timeAnim.setTimeListener(listener);
list.add(leftAnim);
// list.add(leftAnim2);
//list.add(rotateAnim);
list.add(timeAnim);
set.playTogether(list);
// Listen for animation Completed event
set.addListener(new AnimatorListener() {
@Override
public void onAnimationCancel(Animator animation) {
int i =1;
Log.v(“tag”, “msg”);
}
@Override
public void onAnimationEnd(Animator animation) {
Log.v(“tag”, “msg”);
//timeAnim.end();
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.v(“tag”, “msg”);
}
@Override
public void onAnimationStart(Animator animation) {
Log.v(“tag”, “msg”);
}
});
// rotateAnim.addListener(new AnimatorListenerAdapter() {
// public void onAnimationEnd(Animator animation) {
// timeAnim.end();
// }
// });
return true;
}
}
When i start the animation without timeAnimator, the animation works and i can see the image going from left to right. But when i add timeAnimator, the “left” animation stops working. However, if i replace left with rotate property, it works smoothly as explained by you above. Is there something i am missing? Does timeAnimator not work with “left” property?
Please let me know.
Thanks
Manoj
Hi Manoj,
Without knowing exactly how you implemented
FpsTimeListener
and what your layout looks like, these sort of errors are not easily found.