来源:《Learn JavaFX 8: Building User Experience and Interfaces with Java 8》
在这一章节中,你将学到:
- JavaFX是什么
- JavaFX的历史
- 如何去写你的第一个JavaFX应用程序
- 如何使用NetBeans集成开发环境处理JavaFX应用程序
- 如何去给JavaFX应用程序传递参数
- 如何启动JavaFX应用程序
- JavaFX应用程序的生命周期
- 如何终止JavaFX应用程序
什么是JavaFX?
JavaFX是一个基于Java的开源框架,用于开发富客户端应用程序。它与市场上的其他框架(例如Adobe Flex 和 Microsoft Silverlight)具有可比性。在Java平台的图形用户界面(GUI,Graphical User Interface)开发技术领域,JavaFX也被视为Swing的继承者。JavaFX库可作为公共Java应用程序编程接口(API)使用。JavaFX包含几个特性使它成为开发富客户机应用程序的首选:
- JavaFX是用Java编写的,它使您能够利用所有Java特性,如多线程、泛型和lambda表达式。您可以使用您选择的任何Java编辑器(如NetBeans)来编写、编译、运行、调试和打包JavaFX应用程序
- JavaFX通过它的库支持数据绑定。
- JavaFX代码可以使用任何支持Java虚拟机(JVM)的脚本语言编写,如Visage、Groovy和Scala。
- JavaFX提供了两种构建用户界面(UI)的方法:使用Java代码和使用FXML。FXML是一种基于xml的可脚本化标记语言,用于声明性地定义UI。Oracle提供了一个名为Scene Builder的工具,它是FXML的可视化编辑器。
- JavaFX提供了一套丰富的多媒体支持,例如回放音频和视频。它利用了平台上可用的编解码器。
- JavaFX允许您在应用程序中嵌入web内容。
- JavaFX为应用效果和动画提供了开箱即用的支持,这对开发游戏应用程序很重要。您可以通过编写几行代码来实现复杂的动画。
在JavaFX API的背后有许多利用Java本地库和可用硬件和软件的组件。JavaFX组件如图1-1所示。
JavaFX中的GUI作为一个场景图被构建。场景图是被称为节点的视觉元素的集合,按照分层的方式排列。使用公共的JavaFX API可以构建场景图。场景图中的节点可以处理用户的输入和用户的姿势。它们可以有结果、变化和状态。场景图中的节点类型包括简单的UI控件,如按钮、文本字段、二维(2D)和三维(3D)的形状、图像、媒体(音频和视频)、web内容和图表。
Prism是一种用于渲染场景图形的硬件加速图形管道。如果硬件加速呈现在平台上不可用,则使用Java 2D作为后备呈现机制。例如,在使用Java 2D进行渲染之前,它会尝试在Windows上使用DirectX,在Mac Linux和嵌入式平台上使用OpenGL。
Glass Windowing Toolkit提供图形和窗口服务,如使用本机操作系统的窗口和计时器。该工具包还负责管理事件队列。在JavaFX中,事件队列由一个被称为JavaFX Application Thread的单一的、操作系统级的线程管理。所有用户输入的事件都在JavaFX Application Thread上分派。JavaFX要求只能在JavaFX Application Thread上修改实时场景图。
Prism使用一个单独的线程(与JavaFX Application Thread不同)来进行呈现过程。Prism通过在下一帧被处理时渲染一帧来加速处理过程。当场景图被修改时,例如在文本框中输入一些文本,Prism就需要重新渲染场景图。使用一个被称为pluse的事件来实现与Prism同步的场景图。当场景图被修改并且需要重新渲染时,一个pluse事件会在JavaFX Application Thread上排队。pluse事件表明场景图与Prism中的渲染层不同步,应该渲染Prism级别的最新帧。pluse事件被限制在每秒60帧的最大值。
Media Engine负责在JavaFX中提供媒体支持,例如,播放音频和视频。它利用了平台上可用的编解码器。Media Engine使用一个单独的线程来处理媒体帧,并使用了JavaFX Application Thread来同步帧和场景图。该媒体引擎基于GStreamer,这是一个开源的多媒体框架。
Web Engine负责处理嵌入在场景图中的网页内容(HTML)。Prism负责呈现网页内容。该Web Engine基于WebKit,这是一个开源的web浏览器引擎。支持HTML5、层叠样式表(CSS)、JavaScript和文档对象模型(DOM)。
Quantum toolkit是Prism、Glass、Media Engine和Web Engine等底层组件的抽象。它还促进了低级组件之间的协调。
在本书中,我们假设您具有Java编程语言的中级知识。还假定您熟悉Java 8中的新特性,如lambda表达式和Time API。
JavaFX的历史
JavaFX最初是由Chris Oliver在SeeBeyond开发的,它被称为F3(Form Follows Function)。F3是一种用于轻松开发GUI应用程序的Java脚本语言。它提供了声明式语法、静态类型、类型推断、数据绑定、动画、2D图形和Swing组件。SeeBeyond被Sun Microsystems收购,F3在2007年更名为JavaFX。Oracle在2010年收购了Sun Microsystems公司。Oracle在2013年开源了JavaFX。
JavaFX的第一个版本发布于2008年第四季度。JavaFX的当前版本是8.0。版本号从2.2跳到了8.0。从Java 8开始,Java SE和JavaFX的版本号将是相同的。Java SE和JavaFX的主要版本也将同时发布。表1-1列出了JavaFX的发布版本。从Java SE 8发布开始,JavaFX就是Java SE运行时库的一部分。从Java 8开始,不需要任何额外的设置就可以编译和运行JavaFX程序。
- 它是JavaFX的最初版本。它使用一种称为JavaFX Script的声明语言来编写JavaFX代码。
- 引入了对JavaFX Mobile的支持。
- 放弃了对JavaFX脚本的支持。它使用Java语言编写JavaFX代码。放弃了对JavaFX Mobile的支持。
- 只介绍了对Mac OS桌面的支持。
- JavaFX版本从2.2升级到8.0。JavaFX和Java SE版本将与Java 8相匹配。
系统需求
你需要在你的电脑上安装以下软件:
- Java Development Kit 8
- NetBeans IDE 8.0 or later
用NetBeans IDE来编译和运行本书中的程序不是必要的。然而,NetBeans IDE具有创建、运行和打包JavaFX应用程序的特殊特性,使开发人员的工作更加轻松。您可以使用任何其他IDE,例如Eclipse、JDeveloper或IntelliJ IDEA。
JavaFX运行时库
所有JavaFX类都被打包在一个名为jfxrt.jar的Java Archive(JAR)文件中。JAR文件位于Java主目录下的jre\lib\ext目录中。
如果在命令行上编译和运行JavaFX程序,则不需要担心在CLASSPATH中设置JavaFX运行时JAR文件。Java 8编译器(javac命令)和启动器(Java命令)自动在CLASSPATH中包含了JavaFX运行时JAR文件。
当您创建Java或JavaFX项目时,NetBeans IDE自动在CLASSPATH中包含JavaFX运行时JAR文件。如果您使用的是NetBeans以外的IDE,您可能需要在IDE CLASSPATH中包含jfxrt.jar,以便从IDE内部编译和运行JavaFX应用程序。
JavaFX源码
有经验的开发人员有时更喜欢查看JavaFX库的源代码,以了解事情是如何在幕后实现的。Oracle提供了JavaFX源代码。我们可以在Java主目录里的Java 8中复制源文件,其文件名为javafx-src.zip。将文件解压到一个目录中,并使用您最喜欢的Java编辑器打开源代码。
你的第一个JavaFX应用
让我们编写您的第一个JavaFX应用程序。它应该在窗口中显示文本“Hello JavaFX”。我将采用循序渐进的方法来解释如何开发这第一个应用程序。我将尽可能少地添加代码行,然后解释代码的功能以及为什么需要它。
创建HelloJavaFX类
一个JavaFX应用程序类必须继承javafx.application包中Application类。您将把您的类命名为HelloFXApp,它将存储在com.jdojo.intro包中。表1-1显示了HelloFXApp类的初始代码。注意,HelloFXApp类此时还不能编译,您将在下一节中修复它。
表 1-1. 你的JavaFX应用程序类将继承javafx.application.Application类
// HelloFXApp.java
package com.jdojo.intro;
import javafx.application.Application;
public class HelloFXApp extends Application {
// Application logic goes here
}
该程序包括一个包声明、一个导入声明和一个类声明。代码中没有类似JavaFX的东西。它看起来像任何其他Java程序。但是,因为HelloFXApp类继承了Application类,您已经满足了JavaFX应用程序的要求之一。
重写start()方法
如果您尝试编译HelloFXApp类,它将在编译时出现以下错误:HelloFXApp is not abstract and does not override abstract method start(Stage) in Application.该错误表明Application类包含一个抽象的start(Stage Stage)方法,该方法在HelloFXApp类中没有被重写。作为一名Java开发人员,您知道接下来要做什么:要么将HelloFXApp类声明为抽象类,要么实现start()方法。下面让我们实现start()方法。关于Application类中的start()方法声明如下:
public abstract void start(Stage stage) throws java.lang.Exception
表1-2展示了修改后的HelloFXApp类代码,它重写了start()方法。
表1-2. 在JavaFX应用程序类中重写start()方法
// HelloFXApp.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.stage.Stage;
public class HelloFXApp extends Application {
@Override
public void start(Stage stage) {
// The logic for starting the application goes here
}
}
在修改后的代码中,您做了两件事:
- 您添加了一个导入语句,以从javafx.Stage包中导入Stage类。
- 您实现了start()方法。方法的throws子句被删除,根据Java中重写方法的规则,这是被允许的。
start()方法是JavaFX应用程序的入口点。它由JavaFX应用程序启动器调用。注意,start()方法传递了一个Stage类的实例,这个实例被称为应用程序的主要舞台(primary stage)。您可以根据需要在应用程序中创建更多的舞台。但是,主要舞台总是由JavaFX运行时为您创建的。
每个JavaFX应用程序类必须继承Application类,并为start(Stage stage)方法提供实现
展示的舞台
与现实世界中的舞台类似,JavaFX舞台用于显示场景。场景具有可视化——如文本、形状、图像、控件、动画和效果——用户可以与之交互,所有基于GUI的应用程序都是如此。
在JavaFX中,主要舞台是场景的一个容器。根据您的应用程序运行环境的不同,舞台的外观和感觉也不同。您不需要对环境采取任何操作,因为JavaFX运行时将为您处理所有细节。例如,如果应用程序是作为桌面应用程序而运行,主要舞台将是一个带有标题栏和一个显示场景的区域的窗口;如果应用程序是在web浏览器中运行一个applet,主要舞台将是浏览器窗口中的嵌入区域。
由应用程序启动器创建的主要舞台没有场景。在下一节中,您将为您的舞台创建一个场景。
您必须展示舞台,才能看到舞台场景中包含的视觉效果。使用show()方法展示舞台。也可以使用setTitle()方法为舞台设置标题。修改后的HelloFXApp类的代码如表1-3所示。
表1-3. 显示JavaFX应用程序类的主要舞台
// HelloFXApp.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.stage.Stage;
public class HelloFXApp extends Application {
@Override
public void start(Stage stage) {
// Set a title for the stage
stage.setTitle("Hello JavaFX Application");
// Show the stage
stage.show();
}
}
启动应用程序
现在可以运行第一个JavaFX应用程序了。你可以使用以下两个选项之一来运行它:
- 在类中不需要有main()方法来启动JavaFX应用程序。当您运行一个继承自Application类的Java类时,如果正在运行的类不包含main()方法,那么Java命令将启动JavaFX应用程序。
- 如果您想通过JavaFX应用程序类中的main()方法来调用Application类的launch()静态方法去启动应用程序。launch()方法接受一个String数组作为参数,它是传递给JavaFX应用程序的参数。
如果使用第一种方法,则不需要为HelloFXApp类编写任何额外的代码。如果您使用的是第二种方法,那么带有main()方法的HelloFXApp类修改后的代码如表1-4所示。
表1-4. 没有场景的HelloFXApp JavaFX应用程序
// HelloFXApp.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.stage.Stage;
public class HelloFXApp extends Application {
public static void main(String[] args) {
// Launch the JavaFX application
Application.launch(args);
}
@Override
public void start(Stage stage) {
stage.setTitle("Hello JavaFX Application");
stage.show();
}
}
main()方法调用了launch()方法,launch()方法将完成一些设置工作,并调用HelloFXApp类的start()方法。start()方法设置主要舞台的标题并显示该舞台。使用下面的命令编译HelloFXApp类:
javac com/jdojo/intro/HelloFXApp.java
使用下面的命令运行HelloFXApp类,将显示一个标题栏窗口,如图1-2所示:
java com.jdojo.intro.HelloFXApp
图1-2. 没有场景的HelloFXApp JavaFX应用程序
窗口的主要区域是空的,这是舞台将展示其场景的内容区域。因为你的舞台还没有场景,所以你会看到一个空白区域。标题栏显示了您在start()方法中设置的标题。
您可以使用窗口标题栏中的“关闭”菜单选项关闭应用程序。在Windows中可以使用Alt + F4来关闭窗口。您可以使用您的平台所提供的任何其他选择来关闭窗口。
Application类的launch()方法在关闭所有窗口或应用程序使用Platform.exit()方法退出之前不会有返回。Platform类在javafx.application包中。
您还没有在JavaFX中看到任何令人兴奋的东西!您需要再等等,直到您在下一节中创建一个场景。
添加main()方法
如前一节所述,Java 8启动器(java命令)不需要main()方法来启动JavaFX应用程序。如果您想运行继承了Application的类,那么java命令将通过自动为您调用Application.launch()方法来启动JavaFX应用程序。
如果您使用NetBeans IDE创建去JavaFX项目并且通过运行JavaFX项目来运行您的应用程序,那么您不需要使用main()方法来启动JavaFX应用程序。但是,当您将JavaFX应用程序类作为一个文件运行时,NetBeans IDE要求您有一个main()方法。比如,通过右键单击选择HelloFXApp文件,并从菜单中选择run file选项。
一些IDEs仍然需要main()方法来启动JavaFX应用程序。因此,本章中的所有示例都将包含main()方法,它将用来启动JavaFX应用程序。
添加一个场景到舞台
在javafx.scene包中Scene类的一个实例代表了一个场景。一个舞台包含了一个场景,一个场景包含了视觉内容。
场景的内容以树状的层次结构排列。在层次结构的顶部是根节点。根节点可能包含子节点,子节点又可能包含它们的子节点,以此类推。您必须有一个根节点来创建一个场景。您将使用VBox作为根节点。VBox代表垂直框,它将子元素垂直排列在一个列中。下面的语句创建了一个VBox:
VBox root = new VBox();
任何继承自javafx.scene.Parent类的节点都可以用作场景的根节点。被称为布局窗格或者容器几个节点,如VBox, HBox, Pane, FlowPane, GridPane或TilePane都可以用作根节点。Group是一个特殊的容器,它将它的子容器分在一组中。
拥有子节点的节点可以提供一个getChildren()方法,该方法返回子节点的一个Observabllist。要向节点添加一个子节点,只需将子节点添加到ObservableList。下面的代码片段向VBox添加了一个Text节点:
// Create a VBox node
VBox root = new VBox();
// Create a Text node
Text msg = new Text("Hello JavaFX");
// Add the Text node to the VBox as a child node
root.getChildren().add(msg);
Scene类包含几个构造函数。您将使用允许您指定根节点和场景大小的构造函数。下面的语句创建一个以VBox为根节点的场景,宽度300px,高度50px:
// Create a scene
Scene scene = new Scene(root, 300, 50);
您需要通过调用Stage类的setScene()方法将场景设置到舞台:
// Set the scene to the stage
stage.setScene(scene);
像这样,您已经使用场景完成了您的第一个JavaFX程序。表1-5包含完整的程序。程序展示如图1-3所示的窗口。
表1-5. 一个具有文本节点场景的JavaFX应用程序
// HelloFXAppWithAScene.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class HelloFXAppWithAScene extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
Text msg = new Text("Hello JavaFX");
VBox root = new VBox();
root.getChildren().add(msg);
Scene scene = new Scene(root, 300, 50);
stage.setScene(scene);
stage.setTitle("Hello JavaFX Application with a Scene");
stage.show();
}
}
图1-3. 一个JavaFX应用程序,其场景具有一个Text节点
完善HelloFX应用程序
JavaFX能够做的事情比您目前看到的多得多。让我们增强第一个程序并添加一些用户界面元素,例如按钮和文本字段。这一次,用户将能够与应用程序交互。使用Button类的实例去创建一个按钮,如下所示:
// Create a button with "Exit" text
Button exitBtn = new Button("Exit");
当单击按钮时,将触发ActionEvent。您可以添加ActionEvent handler(异步消息处理)来处理事件。使用setOnAction()方法为按钮设置ActionEvent handler。下面的语句为按钮设置ActionEvent handler,用于终止应用程序。您可以使用lambda表达式或匿名类来设置ActionEvent handler。下面的代码片段展示了这两种方法:
// Using a lambda expression
exitBtn.setOnAction(e -> Platform.exit());
// Using an anonymous class
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
...
exitBtn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
Platform.exit();
}
});
表1-6中的程序展示了如何向场景添加更多的节点。程序使用Label类的setStyle()方法将Label的填充颜色设置为蓝色。稍后我将讨论在JavaFX中使用CSS。
表1-6. 在JavaFX应用程序中与用户交互
// ImprovedHelloFXApp.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ImprovedHelloFXApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
Label nameLbl = new Label("Enter your name:");
TextField nameFld = new TextField();
Label msg = new Label();
msg.setStyle("-fx-text-fill: blue;");
// Create buttons
Button sayHelloBtn = new Button("Say Hello");
Button exitBtn = new Button("Exit");
// Add the event handler for the Say Hello button
sayHelloBtn.setOnAction(e -> {
String name = nameFld.getText();
if (name.trim().length() > 0) {
msg.setText("Hello " + name);
} else {
msg.setText("Hello there");
}
});
// Add the event handler for the Exit button
exitBtn.setOnAction(e -> Platform.exit());
// Create the root node
VBox root = new VBox();
// Set the vertical spacing between children to 5px
root.setSpacing(5);
// Add children to the root node
root.getChildren().addAll(nameLbl, nameFld, msg, sayHelloBtn, exitBtn);
Scene scene = new Scene(root, 350, 150);
stage.setScene(scene);
stage.setTitle("Improved Hello JavaFX Application");
stage.show();
}
}
改进后的HelloFX程序展示如图1-4所示的窗口。窗口包含两个标签、一个文本字段和两个按钮。该场景使用VBox作为根节点。在文本字段中输入您的姓名,然后单击Say Hello按钮以查看一条Hello消息。单击Say Hello按钮而不输入姓名,将展示消息Hello there。应用程序在Label控件中展示一条消息。单击Exit按钮以退出应用程序。
图1-4. 一个在场景中几乎没有控件的JavaFX应用程序
使用NetBeans IDE
暂略