原文地址:DOC-01-06 使用FXML进行用户界面设计 | JavaFX中文资料
使用FXML进行用户界面设计
7 七, 2015 官方文档翻译 javafxchina Permalink
本教程展示了使用JavaFX FXML的好处,它基于XML语言并提供了将用户界面与程序逻辑代码分离的架构。
如果你是从头开始学习的本文档,那么你已经了解如何使用JavaFX来创建一个登录应用程序。在这一节,你将使用FXML来创建同样的登录用户界面,将程序的界面设计与业务逻辑分离,这样使得代码更便于维护。你在本教程中将要创建的登录用户界面如图6-1所示。
图6-1 登录用户界面
在本教程中使用的编程工具是NetBeans IDE。请确保你使用的NetBeans版本支持JavaFX8 。参考JavaSE下载页面的”Certified System Configurations”部分来了解更多细节信息。
创建工程
你的首要任务是在NetBeans IDE中创建一个JavaFX FXML工程:
1. 在”File”菜单中选择”New Project”
2. 在”JavaFX”程序类别中,选择”JavaFX FXML Application”。点击”Next”
3. 将工程命名为”FXMLEXample”并点击”Finish”
NetBeans IDE将打开一个FXML工程,其中包括了基本的Hello World应用程序代码。程序中包括如下三个文件:
● java。此文件中包括一个FXML应用程序所需的标准Java代码。
● fxml。这是FXML源文件,在其中你可以定义用户界面。
● java。这是用于处理鼠标和键盘输入的控制器文件。
1. 将java重命名为FXMLExampleController.java,使得其名称更能代表此程序的含义。
● 在Projects窗体中,右键单击java并且选择”Refactor”菜单,然后选择”Rename”菜单。
● 输入”FXMLExampleController”,然后单击”Refactor”
2. 将fxml重命名为fxml_example.fxml
● 右键单击fxml并且选择”Rename”菜单。
● 输入”fxml_example”并且点击”OK”。
加载FXML源文件
你需要编辑的第一个文件是FXMLExample.java文件。此文件中包含了创建应用程序主类、定义stage和scene的代码。针对FXML,此文件中使用了FXMLLoader类,它用于加载FXML源文件并返回其代表的图形界面元素(译者注:目前代码还无法运行,因为FXML内容为空会在运行时报错)。
根据例6-1中的粗体字部分进行代码修改。
例6-1 FXMLExample.java
1 2 3 4 5 6 7 8 | @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("fxml_example.fxml")); Scene scene = new Scene(root, 300, 275); stage.setTitle("FXML Welcome"); stage.setScene(scene); stage.show(); } |
在创建scene时设置它的高和宽是一个不错的实践,在本例中设置为了300*275;否则scene的默认大小是能满足其内容显示的最小大小。
修改导入语句
下面来编辑fxml_example.fxml文件。此文件定义了在程序启动时显示的用户界面。首先需要做的事情是修改import语句,使你的代码如例6-2所示。
例6-2 XML声明和Import语句
1 2 3 4 5 6 | <?xml version="1.0" encoding="UTF-8"?> <?import java.net.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.*?> |
和在Java中一样,类名需要被完全限定(包括包名),否则使用import语句来进行导入,正如例6-2所示。如果你喜欢,可以使用指向多个类的import元素。
创建一个GridPane布局
NetBeans生成的Hello World应用程序采用了AnchorPane布局。对于登录表单来说你将使用GridPane布局,因为它允许你创建一个灵活的行列网格来布局控件。
移除AnchorPane布局及其子节点,将其替换为例6-3中的GridPane。
例6-3 GridPane布局
1 2 3 4 | <GridPane fx:controller="fxmlexample.FXMLExampleController" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <padding><Insets top="25" right="25" bottom="10" left="25"/></padding> </GridPane> |
(译者注:目前代码还无法运行,因为在fx:controller属性所指定的FXMLExampleController类尚未编写完毕,如果你希望此时看到运行效果,将fx:controller=”fxmlexample.FXMLExampleController”删除即可运行查看)。
在此应用程序中,GridPanle是FXML文件的根元素,它有两个属性。fx:controller属性用于指定事件处理控制器。xmlns:fx属性是必须的,它用于指定fx命名空间。
代码中的其余部分设置了GridPane的对齐方式和间隔大小。alignment属性将grid中默认的靠左上对齐改为居中对齐。gap属性用于管理行或列之间的间隔,而padding属性管理GirdPane边缘周围的间隔大小。
当窗体大小被改变时,在GridPane中的节点将会根据其布局约束来自动调整大小。在本例中,所有的控件在你缩放窗体大小时均将保持居中显示。padding属性将确保在窗口被缩小时GirdPane周围仍能留有间隔。
增加文本和密码域
回顾一下图6-1,你会发现登录表单需要一个”Welcome”标题以及用于收集用户信息的文本和密码域。例6-4中的代码是GridPane布局的一部分,它们必须放到</GridPane>语句之前:
1 2 3 4 5 6 7 8 9 10 11 | <Text text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/> <Label text="User Name:" GridPane.columnIndex="0" GridPane.rowIndex="1"/> <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/> <Label text="Password:" GridPane.columnIndex="0" GridPane.rowIndex="2"/> <PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="2"/> |
第一行创建了一个Text对象并将其text值设置为Welcome。GridPane.columnIndex和GridPane.rowIndex属性指定了Text控件在网格中的位置。注意在网格布局中表示行和列的值均从0开始,将Text控件放到(0,0)表示将其放到第1列第1行。GridPane.columnSpan设置为2表示Welcome标题将在网格中横跨两列。由于后面的教程将使用样式表将Text的字体放大到32point,因此需要预留出空间。
第二行在网格的第0列第1行创建了一个Label对象,其文字内容为”User Name”,在第1列第1行创建了TextField对象。类似地,后续创建了另一个Label及其对应的PasswordField对象,并添加到网格中。
在使用网格布局时 ,你可以显示网格线以便于调试。在这里,通过在<padding></padding>之后之后增加<gridLinesVisible>true</gridLinesVisible>元素来设置gridLinesVisible属性为true 。当你运行程序时,将会看到展示出行列和gap属性效果的网格线,如图6-2所示。
(译者注:注意需暂时删除fx:controller=”fxmlexample.FXMLExampleController”)
图6-2 带有网格线的登录表单
增加按钮和文本
最后需添加的两个控件分别是用于提交数据的Button控件和当用户点击按钮时用于显示信息的Text控件。在</GridPane>之前添加例6-5所示的代码:
例6-5 HBox、Button和Text
1 2 3 4 5 6 7 8 | <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button text="Sign In" onAction="#handleSubmitButtonAction"/> </HBox> <Text fx:id="actiontarget" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" GridPane.rowIndex="6"/> |
HBox需要为按钮控件设置与GridPane 布局中的其它控件不同的对齐方式。Alignment属性被设置为了botton_right,这样会将其中的节点靠右下角对齐。HBox被放到网格中的第1列第4行。
HBox下有一个子节点:一个带有”Sign In”文字的Button,其onAction 属性被设置为handleSubmitButtonAction()。FXML提供了快速定义用户界面的方式,但是并没有提供实现应用程序行为的方式。你需要在Java代码中实现handleSubmitButtonAction()方法所表示的行为,这部分内容将会在下一节”增加事件处理代码”中来介绍。
为一个元素设置fx:id属性,如Text控件的代码所示,会在当前文档的命名空间中创建一个对应的变量,你可以通过它在代码中引用对应的控件。定义一个controller域并不是必须的,不过它可以帮助你明确控制器(Controller)和FXML标记是如何关联起来的。
增加处理事件代码
现在来让Text控件在用户点击按钮时显示一条信息。你需要修改FXMLExampleController.java文件。删除NetBeans IDE默认产生的代码,并使用例6-6中的代码替换之。
例6-6 FXMLExampleController.java
1 2 3 4 5 6 7 8 9 10 11 12 | package fxmlexample; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.text.Text; public class FXMLExampleController { @FXML private Text actiontarget; @FXML protected void handleSubmitButtonAction(ActionEvent event) { actiontarget.setText("Sign in button pressed"); } } |
@FXML注解用于标记由FXML使用的非公开的控制器成员属性和事件处理方法。handleSubmtButtonAction方法在用户点击按钮时将actiontarget的文本设置为”Sign in button pressed”。
现在你可以运行程序来查看完整的用户界面效果。图6-3展示了当你在两个信息域中输入内容并且点击”Sign in”按钮时的结果。如果你在运行过程中遇到了任何问题,你可以和FXMLLogin样例对比一下代码。
图6-3 FXML登录窗体
使用脚本语言来处理事件
除了使用Java代码来创建事件处理器(Event Handler)之外,你还可以使用脚本语言来创建,只要这些脚本语言提供与JSR 223规范兼容的引擎即可。例如JavaScript、Groovy、Jython、Clojure。
下面我们试试使用JavaScript。
1. 在fxml文件中的XML doctype声明之后添加JavaScript声明。
1 | <?language javascript?> |
2. 在Button元素中,修改调用的函数名称,如下所示:
onAction=”handleSubmitButtonAction(event);”
3. 从GridPane标签中移除fx:controller属性,并在其下面添加<script>标签,在其中增加JavaScript函数,如例6-7所示:
例6-7 FXML中的JavaScript
1 2 3 4 5 6 7 | <GridPane xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <fx:script> function handleSubmitButtonAction() { actiontarget.setText("Calling the JavaScript"); } </fx:script> |
另外,你也可以将JavaScript代码放到外部文件中(例如fxml_example.js),并且使用如下方式将其引用进来:
1 | <fx:script source="fxml_example.js"/> |
运行效果如图6-4所示。
图6-4 使用JavaScript的Login应用程序
如果你考虑在FXML中使用脚本语言,请注意IDE有可能不支持在调试时进行对脚本代码的单步跟踪调试。
使用CSS来美化程序
最后一个任务是使用层叠样式表(Cascading Style Sheet,CSS)来使登录程序外观变得更具吸引力。
1. 创建一个样式表。
● 在Project窗体中,右键单击Source Packages下的fxmlexample文件夹并且选择”New”菜单,然后选择”Other”菜单。
● 在New File对话框中选择”Other”,然后选择”Cascading Style Sheeet”并且单击”Next”。
● 输入”Login”并且单击”Finish”。
● 将css文件的内容复制到你的CSS文件中。Login.css文件在可下载的LoginCSS.zip文件之中。要了解CSS文件中的样式类的信息,请参考《使用JavaFX CSS美化表单》(译者注:即上一章)
2. 右击下面的灰色亚麻质地的背景图jpg,另存为到fxmlexample文件夹中(译者注:上一章中也用到了此background.jpg,可以将其复制过来)。
3. 打开fxml文件,并且在FXML中GridPane结束标记前添加一个stylesheets标签,如例6-8所示。
例6-8 样式表
1 2 3 4 | <stylesheets> <URL value="@Login.css" /> </stylesheets> </GridPane> |
在URL中的样式表名称之前的@标志表示对应的样式表与FXML文件放在同一文件夹下。
1. 要为GridPane使用root样式,在GridPane布局标签中添加一个styleClass属性,如例6-9所示。
例6-9 为GridPane增加样式
1 2 3 | <GridPane fx:controller="fxmlexample.FXMLExampleController" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" styleClass="root"> |
2. 为”Welcome” Text对象创建一个值为”welcom-text”的 ID,这样它会使用在CSS文件中定义的#welcome-text样式,如例6-10所示。
例6–10 Text ID
1 2 3 | <Text id="welcome-text" text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/> |
3. 运行程序。图6-5展示了添加样式后的应用程序效果。如果你在运行过程中遇到任何问题,请对比下载的FXMLExample.zip文件中的代码内容。
图6-5 增加了样式的登录应用程序
(译者注:实际上这样仅仅能修改背景和按钮的样式,Welcome文本的样式并无法修改,你需要在FXML中为其添加fx:id属性,而且上之前的例子中给出了一个错误的引导,如果使用”welcome-text”作为其id,程序将无法运行通过,将会得到 javafx.fxml.LoadException: Invalid identifier异常;因此你需要将id修改为”welcometext”,并在对应的Login.css文件中,将#welcome-text修改为#welcometext。)
了解更多
现在你应该对FXMl非常熟悉了,你可以看看”FXML的介绍”,它提供了关于构成FXML语言的元素的更多信息。该文档包含在了API文档的javafx.fxml包之中。
你也可以尝试使用JavaFX Scene Builder来打开fxml_example.fxml文件并且进行一些修改。这个工具提供了可视化的布局环境来设计JavaFX应用程序的UI,并且能自动生成布局对应的FXML代码。请注意FXML文件在保存时可能会被重新格式化。参考《开始使用JavaFX Scene Builder(Getting Started with JavaFX Scene Builder)》来了解更多关于此工具的信息。《JavaFX Scene Builder用户指南(JavaFX Scene Builder User Guide)》 中的”使用CSS设计皮肤以及CSS分析器(The Skinning with CSS and the CSS Analyzer)”部分也提供了如何使用JavaFX Scene Builder工具来为你的JavaFX FXML布局设计皮肤的信息。