FXML是JavaFX 2.0提供的新技术。你可能会问“什么是FXML?”,“对我来说有什么用?”。 FXML是一种在JavaFX应用程序中定义用户界面的,基于XML的声明性标记语言。FXML非常适合用来静态布局,如表单、控件和表格。使用FXML,您还可以通过脚本动态地构建布局。
FXML的优势之一是基于XML,是大多数开发人员所熟悉的,尤其是Web开发人员和使用其他RIA平台的开发人员。另一个优点是,FXML是不是编译语言,你不需要重新编译代码就可看到您所做的更改。FXML的第三个优势是很容易看到应用程序的场景结构图。反过来,这更容易完成开发团队成员之间合作的用户界面。
要比较JavaFX和FXML,看看如图1所示的用户界面。构成这个应用程序的场景图包括一个顶部区域和一个中心区域的边框式布局,每个区域都包含一个标签 。
图1 边框窗格的简单例子
例1 JavaFX场景图
BorderPane border=new BorderPane();
Label toppanetext =new Label(“Page Title”);
border.setTop(toppanetext);
Label centerpanetext =new Label(“Some data here”);
border.setCenter(centerpanetext);
例2 FXML场景图
<BorderPane>
<top>
<Label text="Page Title"/>
</top>
<CENTER>
<Label text="Some data here"/>
</CENTER>
</BorderPane>
展示FXML优势的最好办法是使用一个例子。本教程介绍了如何创建如图2所示的登录用户界面。
图2 登录用户界面
在开始之前,先来熟悉一下登录用户界面,它如图 3所示。用户界面使用了一个包含两个区域的边框布局 。顶部区域包含一个内有“Login Example”字样并附图像的文本的堆式布局。中心区域包含一个内有标签,文本框,密码框和按钮的网格式布局。
图3 登录用户界面的布局
要创建这样的登录用户界面,您将完成以下任务:
准备工作
本教程使用NetBeans IDE。请确保您使用的NetBeans IDE版本支持JavaFX 2.0。有关详细信息,请参阅系统需求。
要完成本教程,你应该熟悉如何使用JavaFX构建一个用户界面。如何使用场景图来完成任务的知识非常有用,因为层次结构的FXML与JavaFX场景图的结构是非常相似的。
设置项目
第一步是在NetBeans IDE中建立JavaFX项目。
1. 从“ 文件”“菜单中选择“新建项目”。
2. 在JavaFX的类别目录中选择基于JavaFX FXML的应用程序 。单击“下一步” 。
3. 以FXMLExample命名项目,然后单击“ 完成 “ 。NetBeans IDE会打开一个有三个文件的项目:FXMLExample.java,Sample.fxml和Sample.java。
4. 右键点击这个链接 (fx_boxback.jpg)下载背景为淡蓝色渐变的图像并保存到您的桌面作为程序的背景。然后将它从桌面拖动到源包下的fxmlexample目录下 。
设置应用基础信息
每个FXML应用程序得了必须包括一些JavaFX代码。这些代码是创舞台和场景并启动应用程序代码所要求的最小的代码单元。
打开FXMLExample.java文件 ,删除由Netbeans IDE生成的代码,例 3中的代码替换它 。
例如3 FXMLExample.java
package fxmlexample;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class FXMLExample extends Application {
@Override
public void start(Stage stage)throws Exception {
stage.setTitle("FXMLExample");
Parent root =FXMLLoader.load(getClass().getResource("fxml_example.fxml"),
ResourceBundle.getBundle("fxmlexample.fxml_example"));
Scene scene = new Scene(root, 226, 264);
stage.setScene(scene);
stage.show();
}
public static void main(String[]args) {
launch(args);
}
}
作为一个JavaFX程序员,上面创建场景和舞台并启动应用程序的代码对你应该是很熟悉的。然而,这一行是涉及到FXML:Parent root= FXMLLoader.load(getClass().getResource(“fxml_example.fxml”),ResourceBundle.getBundle(“fxmlexample.fxml_example”));
FXMLLoader.load()方法从资源文件fxml_example.fxml中加载对象的层次结构,并把它分配到的名为root变量。getResources参数增加一个资源包到用户界面中的字符串中,这会使本地化的任务更容易。在接下来的两节,会再次回到的资源包属性文件,并创建FXML源文件 。
总体而言,一个场景对象被创建,并赋到场景图的根变量 。在FXML标记的根元素,成为场景图的根。
创建属性文件
最好的做法是在属性文件中指定用户界面的文本字符串显示内容。按照以下的步骤来创建一个登录用户界面的属性文件。
1.在“项目”窗口中,右键单击源包下的fxmlexample文件夹,然后选择“新建”,再选择“其他”。
2.在新建文件对话框,单击“其他”,然后再单击“属性文件”,再次单击“下一步”。
3.输入fxml_example 作为文件名 ??,然后单击完成。IDE将打开一个扩展名. properties的文件。
4.输入资源名称和它们的值,如例4所示。
例4 fxml_example.properties中的资源名称
loginExample=Login Example
signIn=Sign in:
userName=Username:
password=Password:
submit=Submit
创建FXML文件
现在创建一个名为fxml_example.fxml 的FXML文件,插入XML声明和import语句。
1.在“项目”窗口中,右键单击fxmlexample文件夹下的Sample.fxml,并选择 “ 重命名”。
2.输入文件fxml_example并点击“确定”。
3.打开fxml_example文件,删除NetBeans IDE生成的代码,并用例5中的代码替换它。
例5声明和import语句
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.*?>
<?import javafx.scene.image.*?>
FXML所有文件必须以XML声明开始。它定义了XML的版本(1.0)和类型的编码(UTF - 8)。
就像在JavaFX中一样,类的名字可以是全名(包括包名),或者也可以使用import语句导入,如例5所示。如果你愿意,你可以使用与类相关的导入 。
定义一个边境窗格布局
接下来,开始构建用户界面。在导入语句后,插入边框式窗格布局,如例6所示。
例6边境窗格布局
<BorderPane fx:controller="fxmlexample.FXMLExampleController"
xmlns:fx="http://javafx.com/fxml">
<top>
</top>
<center>
</center>
</BorderPane>
在这个例子中,场景图的根是BorderPane布局类,它定义两个区域,顶部和中心。
FX:controller属性标识了一个控制器文件,它必须为该文件的根元素。在本教程的后续部份您将了解更多有关控制器的内容。
Xmlns:fx =“http://javafx.com/fxml”属性将命名空间映射到http://javafx.com/fxml链接。您必须在FXML的根元素中声明命名空间。这个属性使您能够使用FXML标签对应的JavaFX API元素。
在图像的堆放文本
现在,窝在堆栈的顶部区域的边界窗格布局窗格中 。堆栈窗格包含重叠的图像,在图 4所示的标签 。
图4边框窗格的顶部区域,包括堆式窗格
堆式窗格的代码如例7所示。将<top> 和</top>之间的代码插入到标签之间。
例7在图像的堆放文本
<StackPane>
<children>
<ImageView>
<image>
<Image url="@fx_boxback.jpg"/>
</image>
</ImageView>
<Labeltext="%loginExample" style="-fx-font: NORMAL 20Tahoma;"/>
</children>
</StackPane>
StackPane布局将它的子节点放置在一个堆内,每新增加一个结点,它就会被放置在前一个结点的上面。这种布局模型提供了一种简单的方法在图像上覆盖文本。
<children>将子结点添加到场景图,这是类似于使用JavaFX中的getChildren()方法。
Image类加载与FXML文件位于相应目录的fx_boxback.jpg文件,ImageView类将图像显示在屏幕上。
标签类有一个文本属性被设为资源名loginExample。在FXML中,资源名称通过%来识别。加载时,FXML启动器用它的值“Longin Example”取代loginExample资源名称。
请注意,FXML定义样式的方法与在JavaFX 代码中定义CSS样式时使用setStyle()的方法类似 。在例7中,标签类使用样式标签来设置字体的为正常,字体为宋体,大小为20点。
另一种定义样式的方法是为场景增加样式表,这和JavaFX一样。使用样式表,可以更容易地在后期给你的应用程序重新设定样式。请参阅本教程使用样式表的部份来了解更多有关为FXMLExample应用程序设定样式表的信息。
添加网格布局和控件
下一步,在边框窗格的中心区域放置一个网格布局。您可以使用网格布局在屏幕在纵向和横向放置控件,如在图5所示。
图5网格窗格在边框窗格中心区
例8中包括网格布局的代码 。将它们插入到<CENTER>和</CENTER>标记之间 。
例8有控件的网格布局
<GridPane alignment="top_center" hgap="8"vgap="8"
style="-fx-padding:40 0 0 0">
<children>
<Labeltext="%signIn"
style="-fx-font: NORMAL 14 Tahoma;"
GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<Labeltext="%userName"
GridPane.columnIndex="0"GridPane.rowIndex="1"
labelFor="$usernameField"/>
<TextFieldfx:id="usernameField" prefColumnCount="10"
GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Labeltext="%password"
GridPane.columnIndex="0" GridPane.rowIndex="2"
labelFor="$passwordField"/>
<PasswordFieldfx:id="passwordField" prefColumnCount="10"
GridPane.columnIndex="1" GridPane.rowIndex="2"
onAction="#handlePasswordFieldAction"/>
<Buttonfx:id="submitButton" text="%submit"
GridPane.columnIndex="1" GridPane.rowIndex="3"
onAction="#handleSubmitButtonAction"/>
<Labelfx:id="buttonStatusText"
GridPane.columnIndex="1" GridPane.rowIndex="4"
style="-fx-text-fill: #ff0000;"/>
</children>
</GridPane>
现在的代码应该看起来很熟悉,但有些新的属性必须解释。GridPane.columnIndex和GridPane.rowIndex属性对应GridPane类setColumnIndex()和setRowIndex()方法。网格的行和列编号从零开始。例如,第一个Label控件的位置是(0,0),这意味着它是在第一行的第一列,或说成左上角。prefColumnCount属性用于TextField和PasswordField控件,来将首选列宽设为10。
为元素分配的fx:id的值会在文档的命名空间中创建一个变量,你可以在后面的代码中引用。变量引用使用$符号来识别。在例8中,TextField的ID是usernameField,这个ID被分配到Label控件的labelFor属性,将会设置TextField的标签。
请注意,PasswordField和Button控件会有一个onAction属性。数字符号#会引用自定义方法,这些方法会在下一节创建 FXMLExampleController类时被定义 。
虽然FXML是一种定义应用程序用户界面结构的简便方法,但是它没有提供一个方法来实现应用程序的行为。这种行为必须通过在下一节中描述的Java代码或在《使用脚本语言》中描述的脚本语言来实现。
添加一个按钮事件
现在,创建一个控制器来处理按钮的事件。这个例子演示了如何将FXML标记与Java编写的事件处理程序关联起来。
1. 在“项目”窗口中,右键单击fxmlexample文件夹下的Sample.java,然后选择“重构”,再选择“重命名”。
2. 输入FXMLExampleController并点击“重构”。
3. 打开FXMLExampleController.java文件,删除NetBeans IDE生成的代码,用例9中的代码替换它。
例9 FX??MLExampleController.java
package fxmlexmaple
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class FXMLExampleController {
@FXML private LabelbuttonStatusText;
@FXML protected voidhandleSubmitButtonAction(ActionEvent event) {
buttonStatusText.setText("Submit button pressed");
}
@FXML protected voidhandlePasswordFieldAction(ActionEvent event) {
buttonStatusText.setText("Enter key pressed");
}
}
@FXML注释用来标识已在FXML中标记过的非公有控制器的成员属性和事件处理方法。主要针对Java编程语言,你也可以使用其他编译语言,比如Scala来实现一个控制器。
现在,您可以运行应用程序。测试在应用程序的这两个编辑框中输入文本,然后点击“提交”。
完整的源代码,你可以下载FXMLExample.zip。
使用脚本语言
作为用Java代码来创建事件处理的替代方案,你可以用任何兼JSR223脚本引擎的脚本语方来创建处理程序。例如JavaScript、Groovy、Jython和Clojure。按如下的步骤编辑FXML文件以外便在登录例子中使用JavaScript。
1.在文件fxml_example.fxml中,在XML声明之后添加JavaScript声明:<?language JavaScript?>
2.在按钮标记中,改变函数的名称,调用看起来如下:OnAction =“handleSubmitButtonAction(event);”
3.更新上PasswordField的标记,看起来像这样:OnAction =“handlePasswordFieldAction(event);”
4.从BorderPane标记中移除fx:controller属性,在一个<script>标签下添加JavaScript函数,如例10所示。
例10FXML中的JavaScript
<BorderPane xmlns:fx="http://javafx.com/fxml">
<fx:script>
function handleSubmitButtonAction(){
buttonStatusText.setText("Calling the JavaScript");
}
functionhandlePasswordFieldAction(event) {
buttonStatusText.text = "More JavaScript";
}
</fx:script>
另外,您可以把JavaScript函数放在一个外部文件中(如fxml_example.js),然后将它像这样包括进来:<fx:script source="fxml_example.js"/>
如果你正在考虑在FXML中使用一种脚本语言,请注意,有的IDE可能不支持通过步进方式调试脚本代码。
使用样式表
作为使用内联样式的替代方案,你可以为场景添加一个样式表,然后为节点设置样式类。按照下列步骤来创建一个样式表,它定义了网格窗格和标签控件的风格。
1.创建样式表。
a. 在“项目”窗口中,右键单击源包目录下fxmlexample文件夹,然后选择“ 新建”,再选择“其他”。
b. 在新建文件对话框,选择“其他”,然后选择“层叠样式表”并单击“下一步”。
c. 输入fxmlstylesheet,然后单击完成。
d. 删除NetBeans IDE生成代码,用例11中的代码替换它。
实例11的内容样式表
@charset "utf-8";
/*
Document : FXMLstylesheet.css
*/
.grid-pane {
-fx-padding: 80 0 0 0;
}
.label {
-fx-font: normal 36px Tahoma;
}
2.打开FXMLExample.java文件,在stage.show()这一行前包含这段代码来为场景增加样式表:scene.getStylesheets().add(“fxmlexample /fxmlstylesheet.css”);
3.在fxml_example.fxml文件中增加样式类。
a. 为<String>元素添加一个import语句:<?import java.lang .* ?>
b. 用例12的代码替换GridPane标记。
12例为网格窗格的样式类
<GridPane alignment="top_center" hgap="8"vgap="8">
<styleClass>
<Stringfx:value="grid-pane"/>
</styleClass>
c. 用例13的代码更换“Sign In”标签的标记。
<Labeltext="%signIn"
GridPane.columnIndex="0" GridPane.rowIndex="0">
<styleClass>
<Stringfx:value="label"/>
</styleClass>
</Label>
当您为对像使用<styleClass>标记时,样式应用于同一类的所有实例,除非类有自己的内联样式??。因此,当您运行FXMLExample应用程序时,范例13的样式不仅对登录标签起作用,而且对用户名和密码标签也同样起作用。这个样式对LoginExample标签不起作用,因为它的内联样式覆盖了样式表 。
总结
下面是在本教程中的信息摘要:
- FXML是JavaFX的组成部分。这是另一种创建JavaFX应用程序用户界面的语言。
- FXML是基于XML的。
- FXML文件作为普通JavaFX项目的一部份包括在classpath中,不会被编译。
- FXML的层次结构与JavaFX场景图的结构密切相关。
- 一个最好的编码实践是以<? XML version=”1.0” encodeing=”utf-8”>作为一个 FXML 文件的开始。
- FXML命名空间必须定义在顶层组件或布局中,以便FXML标签可以在整个文件范围内被访问。例如:<rootElement XMLNS:FX =“http://javafx.com/fxml”>
- 通过提取资源包字符串,FXML可以在加载时进行本地化。资源替换通过%操作符标识。
- 在FXML中定义样式类似于在Java代码中通过setStyle()方法定义CSS样式。您可以使用内联样式 ??或创建样式表。
- 为元素分配fx:id值以便在文档的命名空间中创建一个变量,它可以被后面的代码引用。变量参考通过$符号来引用。
- 除了Java,FXML还支持其他编译语言,比如Scala,来实现控制器。
- FXML支持任何JSR223兼容的脚本引擎。这些语言包括JavaScrip、Groovy、Jython和Clojure。