Android Property Animations: Controlling Animation Flow

In today’s post I will be talking about controlling animation flow. Using the Animator API you can start, stop and cancel animations. A little reported addition in the Kitkat API level 19 allows you to also pause and resume animations. In this post I will take you through the animation flow controls and some methods that let you inspect the status of the animation.

Animation Flow

In the previous tutorials we have already encountered the Animator.start method multiple times. This method is used to start an animation from the beginning. The method is only one of the set of methods that control the animation flow. The complete set of methods is shown below.

Animator.start()   // start the animation from the beginning
Animator.end()     // end the animation
Animator.cancel()  // cancel the animation 
Animator.pause()   // added in API 19; pause the animation
Animator.resume()  // added in API 19; resume a paused animation

As already stated, the start method starts the animation from the beginning. If the animation has a startDelay that is greater than zero then the animation will start after the delay has passed.

There are two ways of stopping a running animation. You can use either the end method or the cancel method. In both cases the animation will stop and can only be restarted by calling start. The difference between end and cancel is the state that the animated objects will be in after the call to the method. When calling cancel the animation will stop in its tracks, leaving the animated objects in an intermediate state. When calling the end method the animation will effectively be fast forwarded into the final state of the animation. All objects will appear the way they would at the end of the animation.

A feature of the new API in the Kitkat realease of Android that has not received much media attention is the fact that animations can now be paused and resumed. Previously, when an animation was cancelled and left in the current state, it could only be restarted from the beginning by calling the start method. Now you can call pause to pause the animation. Pausing will have the same visual effect as cancel. The difference is that a call to resume will resume the animation from the paused state.

Let’s see how this looks in practice. We create a new activity and hold the animation as a private member. We initialise the animation in the onCreate method.

  private ObjectAnimator anim;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.property_animations_flow);

    ImageView someImage = (ImageView) findViewById(R.id.some_image);

    anim = ObjectAnimator.ofFloat(someImage, "rotation", 0, 360);
    anim.setDuration(1000);
    anim.setRepeatCount(5);
    anim.setRepeatMode(ObjectAnimator.RESTART);
  }

The animation is a simple rotation animation that repeats a full 360 degrees image rotation five times. The image that is rotated has been defined in the layout XML file and given the id some_image. The layout also defines 5 buttons labelled Start, End, Cancel, Pause, and Resume. The buttons are linked to the following five methods.

  public void startAnimation(View view) {
    anim.start();
  }

  public void endAnimation(View view) {
    anim.end();
  }

  public void cancelAnimation(View view) {
    anim.cancel();
  }

  public void pauseAnimation(View view) {
    anim.pause();
  }

  public void resumeAnimation(View view) {
    anim.resume();
  }

These methods will simply call the corresponding methods on the ObjectAnimator anim. The two animated gifs below demonstrate the behaviour. The left movie shows the difference between end and cancel. Note how cancel leaves the image in the rotated position while end will advance the image to its final state.

The movie on the right shows the effect of pause and resume. Note how a call to pause will stop the animation in its tracks, just like a call to cancel. But now we can resume the animation by calling resume.

The difference between calling end and cancel on an Animator

The difference between calling end and cancel on an Animator

Pausing and resuming an Animator

Pausing and resuming an Animator

Querying Animation Status

Sometimes you would like to query the state of an animation. This can be done using the following three methods.

boolean isStarted()  // added in API 14
boolean isRunning()
boolean isPaused()   // added in API 19

isStarted will return true after the start method has been called but the animation has not finished or has not been cancelled. Note that isStarted requires a minimum API of 14. The method will return true even during the duration of any initial start delay. This is in contrast to the return value of isRunning. isRunning will return true only when the animation is actually running and has not finished.

The isPaused method was added in API 19. This accounts for the fact that an animation can be paused and resumed. If it is paused then isPaused will return true, otherwise is will return false.

To demonstrate the outcome of these inspector methods, we will extend the example by displaying three text fields that show the status of the animation. These are defined as TextView members of the Activity class

  private TextView isStartedText;
  private TextView isRunningText;
  private TextView isPausedText;

In the onCreate method we add three lines that retrieve the text views from the layout.

    isStartedText = (TextView) findViewById(R.id.status_is_started);
    isRunningText = (TextView) findViewById(R.id.status_is_running);
    isPausedText = (TextView) findViewById(R.id.status_is_paused);

We create a method that will update the content of the text fields from the animation status.

  public void setStatusTexts() {
    isStartedText.setText("isStarted = " + anim.isStarted());
    isRunningText.setText("isRunning = " + anim.isRunning());
    isPausedText.setText("isPaused = " + anim.isPaused());
  }

We then call the setStatusTexts method after initialization and every time we modify the animation flow. For example, the method cancelAnimation is changed as follows.

  public void cancelAnimation(View view) {
    anim.cancel();
    setStatusTexts();
  }

The results can be seen in the left movie below. Again, I created one movie for the End and Cancel control and one movie for the Pause and Resume control. Note that the state of the animation is identical after pressing End or Cancel even though the visual appearance of the animated object is different. This means that you can’t distinguish between animations that have been stopped by calling end and those that have been stopped by calling cancel. In both cases isStarted and isRunning will return false.

Status of Animation when End and Cancel is pressed.

Status of Animation when End and Cancel is pressed.

Status of the Animation when Pause and Resume is pressed

Status of the Animation when Pause and Resume is pressed

The right movie above shows the effect of pausing and resuming the animation. When pausing an animation isPaused will return true. After resuming the animation isPaused will again return false. Note that the animation status does not change when the animation finishes naturally. Surely, isStarted and isRunning should return false after the end of the animation. The answer is, they would return false is we called them. In the example we are not updating the status texts at the end of the animation because we have don’t know when the animation has finished. To be able to update the status texts, or any other part of the application, when the animation status changes we will need the AnimatorListener and the AnimatorPauseListener. Using listeners in property animations will be the topic of the next post in this series.

The complete code for this tutorial can be downloaded from GitHub.

Follow the author

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>