Using Transitions for Animation in Oracle's JavaFX 2.0

Downloads:
Download: JavaFX 2.0 Beta

Download: NetBeans IDE

Download: NetBeans Sample Project (zip file)

Animate the nodes in your scene the easy way.

JavaFX 2.0 from Oracle is an API and runtime for creating Rich Internet Applications (RIAs).JavaFX was introduced in 2007, and version 2.0 beta was released in 2011. One of the advantages of JavaFX 2.0 is that the code may be written in the Java language, using mature and familiar tools.This article focuses on using JavaFX 2.0 transitions to animate visual nodes in the user interface (UI).

JavaFX comes with its own transition classes, shown in Figure 1, whose purpose is to provide convenient ways to do commonly used animation tasks. These classes are located in the javafx.animation package. This article contains an example of using the TranslateTransition class to animate a node, moving it back and forth between two positions in the UI.

Transitions Table

Figure 1: Table of JavaFX Transition Classes

Overview

To help you learn how to use the TranslateTransition class, an example named TransitionExampleX will be employed.As shown in Figure 2, this example contains a couple of buttons, a couple of rectangles, and a circle that you'll animate a little later.

The TransitionExampleproject that you'll download in the next section contains starter code for this example, and it has an appearance at runtime similar to Figure 2.During the course of this article, you'll modify the code to implement the animated behavior of the TransitionExampleX project, which is also available to download.



Figure 2: Screen Capture of TransitionExampleX

Figure 2: Screen Capture of the TransitionExampleXExample Program

When you click the Play button, the text on the button changes to Pause, as shown in Figure 3, and the ball moves back and forth between the paddles indefinitely.



Figure 3: Screen Capture of the TransitionExampleX After Clicking the Play Button

Clicking the Pause button causes the animation to pause and the text on the button to change to Play.

Obtaining and Running the TransitionExample Example

  1. Download the NetBeans project (zip file) that includes the TransitionExample program
  2. Expand the project into a directory of your choice.
  3. Start NetBeans, and select the File -> Open Project menu.
  4. From the Open Project dialog box, navigate to your chosen directory and open the TransitionExample project, as shown in Figure 4. If you receive a message dialog stating that the jfxrt.jar file can't be found, click the Resolve button and navigate to the rt/lib folder subordinate to where you installed the JavaFX 2.0 SDK.
  •  

Note: You can obtain the NetBeans IDE from the NetBeans site.

Figure 4: Opening the TransitionExampleProject in NetBeans

  1. To run the application, click the Run Project icon on the toolbar, or press the F6 key.The Run Project icon has the appearance of the Play button on a DVD player, as shown in Figure 5.


Figure 5: Invoking the TransitionExampleProgram in NetBeans



The TransitionExample application should appear in a window, as shown in Figure 6.

Figure 6: Screen Capture of the TransitionExampleProgram

You'll notice that clicking the Play and Stop buttons has no effect.Your mission will be to add code that implements the behavior described previously.Here are steps you can follow to implement this behavior:
 

Step 1: Create a TranslateTransition Instance to Animate the Node

To cause the ball to move (also known as translate) between two different positions in the scene, we'll create an instance of the TranslateTransition class. Take a look at the code in Listing 1, which shows the starter code for this example and is located in the TransitionExampleMain.java file.

Listing 1: TransitionExampleMain.java

package transitionexample.ui;

import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.builders.ButtonBuilder;
import javafx.builders.CircleBuilder;
import javafx.builders.GroupBuilder;
import javafx.builders.RectangleBuilder;
import javafx.builders.SceneBuilder;
import javafx.builders.TranslateTransitionBuilder;
import javafx.builders.VBoxBuilder;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TransitionExampleMain extends Application {

  /**
   * A reference to the program's Scene
   */

  Scene scene;


  /**
   * A VBox that contains the control buttons
   */

  VBox buttonsContainer;

 
  /**
   * A TranslateTransition that will animate the ball
   */

  TranslateTransition transition;

  
  /**
   * The play/pause button
   */

  Button playButton;


  /**
   * The Stop button
   */

  Button stopButton;


  /**
   * Strings that hold possible values for text on the Play button
   */

  String playText = "Play";
  String pauseText = "Pause";

  /**
   * The StringProperty to which the text for the Play button will be bound
   */

  StringProperty playButtonText = new StringProperty(playText);

  /**
   * Invoked as a result of being launched by the Application.launch() call
   * in the main method below.
   * @param stage
   */

  @Override

  public void start(Stage stage) {

    /**
     * The ball, whose coordinates will be changed by the TranslateTransition
     */

    Circle ball = CircleBuilder.create()
      .radius(10)
      .centerX(30)
      .centerY(130)
      .fill(Color.WHITE)
      .stroke(Color.BLACK)
      .strokeWidth(2)

    .build();


    // Over a duration of 1500 milliseconds, the translateX variable of
    // the ball node is increased from 0 to 440.  At the end of the animation,
    // the transition reverses back to its starting position (autoReverse is
    // set to "true").  The cycleCount property specifies how many times to
    // repeat the transition at the end of the animation.  Here, it is set to
    // Transition.INDEFINITE, meaning it will not stop repeating until the
    // pause() or stop() methods are called, or the application is closed.

    transition = TranslateTransitionBuilder.create()
      .duration(new Duration(1500.0))
      .node(ball)

      // TO DO: Insert code to set the fromX property to 0
      // TO DO: Insert code to set the toX property to 440

      .interpolator(Interpolator.LINEAR)

      // TO DO: Insert code to set the autoReverse property to true

      .cycleCount(Animation.INDEFINITE)
    .build();


    playButton = new ButtonBuilder.create()       
    .onAction(new EventHandler<javafx.event.ActionEvent>() {        
      @Override public void handle(javafx.event.ActionEvent e) {          
       if (transition.getStatus() == Animation.Status.PAUSED) {

            // TO DO: Insert code here to start the animation         
            // TO DO: Insert code here to change Play button text to pauseText

          }

          else {

            // TO DO: Insert code here to pause the animation         
            // TO DO: Insert code here to change Play button text to playText

          }
       }     
      })
    .build();

    stopButton = new ButtonBuilder.create()      
     .text("Stop")
     .onAction(new EventHandler<javafx.event.ActionEvent>() {
        @Override public void handle(javafx.event.ActionEvent e) {

          // TO DO: Insert code here to stop the animation
          // TO DO: Insert code here to change Play button text to playText
          }
       }     
      })
    .build();

    Scene scene = new SceneBuilder.create()
      .width(500)
      .height(250)
      .root(new GroupBuilder.create()
        .children(
          new RectangleBuilder.create()
            .x(10)
            .y(120)
            .width(10)
            .height(20)
            .fill(Color.GRAY)
            .stroke(Color.BLACK)
          .build(),
          new RectangleBuilder()
            .x(480)
            .y(120)
            .width(10)
            .height(20)
            .fill(Color.GRAY)
            .stroke(Color.BLACK)
          .build(),
          ball,
          buttonsContainer = new VBoxBuilder.create()
            .layoutX(100)
            .layoutY(15)
            .alignment(Pos.CENTER)
            .spacing(5)
            .children(
              playButton,
              stopButton
            )
          .build()
        )
      .build()
      )
    .build();

    stage.setScene(scene);
    stage.setVisible(true);

    // Bind the Play button text to a StringProperty variable
    playButton.textProperty().bind(playButtonText);

    // Center the horizontal location of the buttons container in the scene
    // Note: This is to demonstrate binding expressions.  Layout containers
    //       would normally be the preferred approach.

    buttonsContainer.layoutXProperty().bind(scene.widthProperty()
        .subtract(buttonsContainer.widthProperty()).divide(2.0));
  }

  /**
   * The main method for this Java application
   * @param args the command line arguments
   */

  public static void main(String[] args) {
    Application.launch(TransitionExampleMain.class, args);
  }
}
 

Using the TranslateTransitionBuilder Class to Build TranslateTransition

The starter code shown in Listing 1 makes use of builder classes in the javafx.builders package of the JavaFX 2.0 API, including the TranslateTransitionBuilder class shown here:

transition = new TranslateTransitionBuilder.create()
 .duration(new Duration(1500.0))
 .node(ball)
 // TO DO: Insert code to set the fromX property to 0
 // TO DO: Insert code to set the toX property to 440
 .interpolator(Interpolator.LINEAR)
 // TO DO: Insert code to set the autoReverse property to true
 .cycleCount(Animation.INDEFINITE)
.build();


Go ahead and fill in the lines indicated by the “TO DO” comments, so the code block shown above turns into the one shown below:

transition = new TranslateTransitionBuilder()
.duration(new Duration(1500.0))
.node(ball)
.fromX(0)
.toX(440)
.interpolator(Interpolator.LINEAR)
.autoReverse(true)
.cycleCount(Animation.INDEFINITE)
.build();


There are many builder classes in the JavaFX API, and their purpose is to enable a declarative-style of programming to create and set the properties of objects.For example, the code that you filled in above creates an instance of the TranslateTransition class, and it populates that instance with properties such as the duration of the animation and the node in the scene graph being animated.As you can see in Listing 1, other builder classes used in this application include ButtonBuilder, CircleBuilder, RectangleBuilder, SceneBuilder and VBoxBuilder. 

Step 2: Define an Event Handler in the Play Button

To make the TranslateTransition start, pause, and stop, you'll define event handlers in the buttons that call the appropriate methods on the TranslateTransition object.For example, the starter code below from Listing 1 contains the builder that creates a Button instance for the Play button.

playButton = new ButtonBuilder.create()
.onAction(new EventHandler<javafx.event.ActionEvent>() {
        @Override public void handle(javafx.event.ActionEvent e) {
          if (transition.getStatus() == Animation.Status.PAUSED) {

            // TO DO: Insert code here to play the animation
            // TO DO: Insert code here to change Play button text to pauseText
          }
          else {

            // TO DO: Insert code here to pause the animation
            // TO DO: Insert code here to change Play button text to playText

          }
        }
      })
.build();


As you did before, fill in the lines indicated by the “TO DO" comments, turning the code block shown above into the one shown below:

playButton = new ButtonBuilder.create()
.onAction(new EventHandler<javafx.event.ActionEvent>() {
        @Override public void handle(javafx.event.ActionEvent e) {
          if (transition.getStatus() == Animation.Status.PAUSED) {
            transition.play();
            playButtonText.setValue(pauseText);
          }
          else {
            transition.pause();
            playButtonText.setValue(playText);
          }
        }
      })
.build();


Using Methods of the Animation Class to Control the Animation

All the transition classes used in Figure 1 are subclasses of the Transition class, which is a subclass of the Animation class.The Animation class has methods such as start() and getStatus() that control and monitor the state of the animation.As a result of the code that you filled in just now, when the button is clicked, the status of the animation is ascertained.If the animation status is PAUSED, the play() method of the animation is invoked; otherwise, the pause() method is called.

Using Binding to Keep Properties Updated

Property binding is a very convenient and powerful feature of the JavaFX API, because it enables you to keep the values of properties automatically updated.The following excerpts from Listing 1 bind the textProperty of the playButton to a StringProperty that holds the text to be displayed in the button:

   String playText = "Play";
   ...
   StringProperty playButtonText = new StringProperty(playText);
   ...
   playButton.textProperty().bind(playButtonText);


As the value of playButtonText is updated by the event handler that you just coded, the text on the button displays the updated value.

There is another use of property binding in Listing 1, which is shown in the following excerpt and demonstrates how to bind expressions:

    buttonsContainer.layoutXProperty().bind(scene.widthProperty()
                    .subtract(buttonsContainer.widthProperty()).divide(2.0)); 

To keep the buttonsContainer node horizontally centered in the application window, the layoutXProperty of one of the nodes in the scene graph is bound to an expression that contains the widthProperty of the Scene.

Note:The excerpt above is for demonstration purposes, because the use of layout containers (in the javafx.scene.layout package) is normally the preferred approach for dynamically positioning nodes within a scene.

Step 3: Define an Event Handler in the Stop Button

To finish the TransitionExample, you'll need to enter some code into the following starter code from Listing 1:

stopButton = new ButtonBuilder.create()
  .text("Stop")
  .onAction(new EventHandler<javafx.event.actionevent>() {     
  @Override public void handle(javafx.event.ActionEvent e) {          
  // TO DO: Insert code here to stop the animation       
  // TO DO: Insert code here to change Play button text to playText     
    }  
  })
.build();
  

The completed code for the Stop button event handler is very similar to what you've already coded in the Play button event handler.Go ahead and fill in the code as shown below, and run the example.

stopButton = new ButtonBuilder.create()
  .text("Stop")
  .onAction(new EventHandler<javafx.event.ActionEvent>() {
    @Override public void handle(javafx.event.ActionEvent e) {
      transition.stop();
      playButtonText.setValue(playText);
    }
  })
.build();


Conclusion

JavaFX 2.0 comes with several transition classes (that extend the Transition class whose purpose is to animate visual nodes in your application.JavaFX also contains many builder classes that provide the ability to express a user interface in a declarative style.In addition, JavaFX has a powerful property binding capability in which properties may be bound to expressions to automatically keep them updated.

See Also


About the Author

jim weaver Jim Weaver is an independent Java and JavaFX developer, author, and speaker with a passion for helping rich-client Java and JavaFX become preferred technologies for new application development.He blogs at http://javafxpert.com, tweets @javafxpert, and may be reached at jim.weaver[at]javafxpert.co