使用 Oracle JavaFX 2.0 中的 Transition 类实现动画效果

下载:
下载JavaFX 2.0 Beta 版

下载NetBeans IDE

下载NetBeans 示例项目(zip 文件)

在场景中轻松实现节点动画效果

Oracle 的 JavaFX 2.0 是用于创建富互联网应用程序 (RIA) 的 API 和运行时。JavaFX 于 2007 年引入,并于 2011 年发布了 2.0 beta 版。JavaFX 2.0 的一个优点是可以使用成熟且熟悉的工具以 Java 语言编写代码。本文重点介绍如何使用 JavaFX 2.0 transition 类在用户界面 (UI) 中实现可视节点动画。

JavaFX 附带了自己的 transition 类,如图 1 所示,其目的是为执行常用动画任务提供便捷。这些类位于 javafx.animation 程序包中。本文包含了一个使用 TranslateTransition 类让一个节点呈现动画效果的示例,使节点在 UI 中的两个位置之间来回移动。

Transition 类表

图 1:JavaFX Transition 类表

概述

为了帮助您了解如何使用 TranslateTransition 类,我们将使用一个名为 TransitionExampleX 的示例。如图 2 所示,此示例包含两个按钮、两个矩形以及一个圆形,稍后您将让这个圆形呈现动画效果。

在下一节中您将下载 TransitionExample 项目,该项目包含这个示例的初始代码,其运行时外观如图 2 所示。在本文中,您将修改该代码以实现 TransitionExampleX 项目(也提供下载)的动画行为。



图 2:TransitionExampleX 的屏幕截图

图 2:TransitionExampleX 示例程序的屏幕截图

单击 Play 按钮后,该按钮上的文本将变为 Pause,如图 3 所示,并且球将在球拍之间来回不停地移动。



图 3:单击 Play 按钮后 TransitionExampleX 的屏幕截图

单击 Pause 按钮会使动画暂停并且按钮上的文本变为 Play

获取并运行 TransitionExample 示例

  1. 下载包括了 TransitionExample 程序的 NetBeans 项目(zip 文件)
  2. 将该项目展开到您选择的目录中。
  3. 启动 NetBeans,选择 File -> Open Project 菜单。
  4. 在 Open Project 对话框中,找到所选目录并打开 TransitionExample 项目,如图 4 所示。如果收到一个声明无法找到 jfxrt.jar 文件的消息对话框,单击 Resolve 按钮并转到 JavaFX 2.0 SDK 安装目录下面的 rt/lib 文件夹。
  •  

:可以从 NetBeans 站点获取 NetBeans IDE。

图 4:在 NetBeans 中打开 TransitionExample 项目

  1. 要运行应用程序,单击工具栏上的 Run Project 图标,或按 F6 键。Run Project 图标的外观类似于 DVD 播放器上的“播放”按钮,如图 5 所示。


图 5:在 NetBeans 中调用 TransitionExample 程序



TransitionExample 应用程序会显示在一个窗口中,如图 6 所示。

图 6:TransitionExample 程序的屏幕截图

您将注意到,单击 PlayStop 按钮没有任何效果。您的任务是添加代码以实现前面所述行为。可按照以下步骤实现此行为:
 

第 1 步:创建 TranslateTransition 实例以实现节点动画效果

为使球在场景中的两个不同位置之间移动(也称为平移),我们将创建 TranslateTransition 类的一个实例。我们来看一下清单 1 中的代码,显示了本示例的初始代码,位于 TransitionExampleMain.java 文件中。

清单 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);
  }
}
 

使用 TranslateTransitionBuilder 类构建 TranslateTransition

清单 1 中所示初始代码利用了 JavaFX 2.0 API 的 javafx.builders 程序包中的 builder 类,包括下面所示的 TranslateTransitionBuilder 类:

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();


来吧,填写“TO DO”注释所指示的行,使上述代码块转换成如下所示的代码:

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


JavaFX API 中有许多构建器类,旨在以声明式编程来创建和设置对象属性。例如,以上所填写的代码将创建 TranslateTransition 类的一个实例,并为该实例填充一些属性,如动画的时长和场景图中要实现动画效果的节点。如清单 1 中所示,该应用程序中所用其他构建器类包括 ButtonBuilderCircleBuilderRectangleBuilderSceneBuilderVBoxBuilder 

第 2 步:在 Play 按钮中定义事件处理程序

为使 TranslateTransition 启动、暂停和停止,您将在按钮中定义调用 TranslateTransition 对象的相应方法的事件处理程序。例如,清单 1 中的以下初始代码包含为 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();


与前面一样,填写“TO DO”注释所指示的行,使上述代码块转换成如下所示的代码:

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();


使用 Animation 类的方法控制动画

图 1 中所用的所有 transition 类都是 Transition 类的子类,而 Transition 类是 Animation 类的子类。Animation 类有一些控制和监视动画状态的方法,如 start()getStatus()。通过刚才所填写的代码,当单击该按钮时,可确定动画的状态。如果动画状态为 PAUSED,将调用动画的 play() 方法;否则,将调用 pause() 方法。

使用绑定持续更新属性

属性绑定是 JavaFX API 的一项非常方便、强大的功能,使您能够让属性的值自动更新。清单 1 中的以下代码片断将 playButtontextProperty 绑定到 StringProperty,后者保存的文本会显示在按钮中:

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


当您刚编写的事件处理程序更新 playButtonText 的值时,按钮上的文本会显示更新的值。

清单 1 中的属性绑定还有一种用途,如以下代码片断所示,它演示了如何绑定表达式:

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

为使 buttonsContainer 节点在应用程序窗口中保持水平居中,我们将场景图中的一个节点的 layoutXProperty 绑定到一个包含 ScenewidthProperty 的表达式。

:以上代码片断旨在用于演示,因为对于在场景内动态定位节点,使用布局容器(在 javafx.scene.layout 程序包中)通常是首选的办法。

第 3 步:在 Stop 按钮中定义事件处理程序

为完成 TransitionExample,需要在清单 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();
  

Stop 按钮事件处理程序的完整代码非常类似于 Play 按钮事件处理程序中编写好的代码。我们来按如下所示填写代码并运行该示例。

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();


总结

JavaFX 2.0 附带了一些 transition 类(这些类扩展了 Transition 类),用于在应用程序中实现可视节点动画。JavaFX 还包含许多构建器类,让您能够以声明方式表达用户界面。此外,JavaFX 还有一项强大的属性绑定功能,可以将属性绑定到表达式中以使属性自动更新。

另请参见


关于作者

jim weaver Jim Weaver 是一位独立的 Java 和 JavaFX 开发人员、作者和演讲者,积极致力于促进富客户端 Java 和 JavaFX 成为新应用程序开发的首选技术。他的博客为 http://javafxpert.com,Tweet 为 @javafxpert,电子邮件联系方式为 jim.weaver[at]javafxpert.co