FXML是JavaFX 2.0新引入的。你可能会问"What is FXML?" 和"Is FXML for me?" FXML 是基于XML的一种声明性标记语言,用来定义应用的用户接口。FXML对于定义静态的布局很便利,诸如form, control, 和table。使用FXML也可以动态构造布局,不过要结合脚本。

 FXML是一个优势是基于XML,所以多数开发者,尤其是web开发者和其他RIA平台的开发者会很熟悉它。另一个优势是FXML不是编译型语言,不需要编译后才能看出变化。第三个好处是可以很简单的看到应用场景的结构。反过来,也就很简单地可以在组内进行合作开发用户接口。

要对比JavaFX和FXML,看图Figure 1   .构成该应用的场景包括一个边框布局,在它的顶部和中间各有一个标签。

Figure 1 Border Pane Simple Example



Description of "Figure 1 Border Pane Simple Example"   




 

Example 1   是相应的JavaFX代码.

Example 1 JavaFX Scene Graph
 
 
BorderPane border = new BorderPane();
Label toppanetext = new Label("Page Title");
border.setTop(toppanetext);
Label centerpanetext = new Label ("Some data here");
border.setCenter(centerpanetext);


Example 2   是相应的FXML.


Example 2 FXML Scene Graph
 
 
<BorderPane>
    <top>
        <Label text="Page Title"/>
    </top>
    <center>
        <Label text="Some data here"/>
    </center>
</BorderPane>


展示FXML优势的最好方法是例子。本指南讲解如何创建Figure 2   中的登陆界面 .


Figure 2 Login User Interface




Description of "Figure 2 Login User Interface"   




开始之前先熟悉一下Figure 3   中的用户接口. 该接口使用了一个包含两部分的边框布局。顶区域包括一个堆栈布局,里面是用文本Label Example覆盖一副图片。

 


Figure 3 Layout of Login User Interface




Description of "Figure 3 Layout of Login User Interface"   




 

要创建该界面,我们需完成以下任务:


准备

本指南使用NetBeans IDE.请确保NetBeans IDE的版本支持JavaFX 2.0。


要完成本文,应熟悉用JavaFX编用户接口。尤其要知道场景的知识,因为FXML的语法结构和JavaFX的场景很像。


建立工程

第一步是建立JavaFX工程。

  1. File菜单选择   New Project
  2. JavaFX   application category中,选择JavaFX FXML Application,点   Next
  3. Finish
  4. 下载浅蓝色渐变的  fx_boxback.jpg   图片到桌面,用来当背景。然后把它拖到fxmlexample文件夹下。


建立基础

每个 FXML 应用必须包含一些JavaFX代码,最少也有创建舞台和场景还有启动的代码。

FXMLExample.java   ,删除NetBeans IDE生成的代码,用下面的代码代替。Example 3   .


Example 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("FXML Example");
        
        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源文件。 总之,场景已经创建,root变量是场景根元素。


创建配置文件

最佳实践是将字符串外部化,把它们放进配置文件。按照以下步骤创建用户登录界面的配置文件。

  1. New   ,然后是Other
  2. Other   ,然后是Properties File   ,点  Next
  3. Finish。
  4. 输入资源名称和值,如 Example 4   .
1.      
    
 Example 4 Resource names in fxml_example.properties 
    loginExample=Login Example
signIn=Sign in:
userName=Username:
password=Password:
submit=Submit



创建FXML文件

现在创建fxml_example.fxml文件并插入XML声明和导入语句。

  1. Rename
  2. OK
  3. 打开fxml_example文件,删除自动生成代码,用下面的取代 Example 5   .
Example 5 Declaration and Import Statements 
    <?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.*?>
<?import javafx.scene.image.*?>

  1. 所有FXML文件必须以XML声明开始。它定义了XML版本(1.0)和编码类型(UTF-8).
    在JavaFX中,类名称可以被完整描述(包括包名),或者是使用导入语句,见 Example 5   .只要你喜欢,你可以完整导入所以相关类。



定义边框布局

下面开始构建用户界面。在导入语句后面插入下面代码 Example 6   .



Example 6 Border Pane Layout
 
  
<BorderPane fx:controller="fxmlexample.FXMLExampleController" xmlns:fx="http:///fxml">
     <top>
     </top>
     <center>
     </center>
</BorderPane>

BorderPane布局类,定义了两快区域,顶部和中部。fx:controller属性定义了控制器文件,必须要声明在FXML根元素中。后面还会更多的了解控制器。xmlns:fx="http:///fxml"属性将命名空间映射到http:///fxml。命名空间也必须声明在FXML根元素中。该属性让你可以使用JavaFX API相关的FXML标签。


在图像上覆盖文字

现在,在边框布局的顶区域放置一个堆栈布局。该布局包括一个标签和被覆盖的图片,见 Figure 4   .



Figure 4 Top Region of Border Pane, Including Stack Pane



Description of "Figure 4 Top Region of Border Pane, Including Stack Pane"   




堆栈布局的代码是 Example 7   .把它们插入在 <top>   和  </top>标签之间。


Example 7 Stack Text Over an Image
 
  
<StackPane>
            <children>    
                <ImageView>
                   <image>
                        <Image url="@fx_boxback.jpg"/>
                   </image>
                </ImageView>
                <Label text="%loginExample" style="-fx-font: NORMAL 20 Tahoma;"/>
            </children>
        </StackPane>

StackPane布局将其孩子结点放进一个栈中,后面的会放在之前的上面。该布局可以很方便的把文本放在图片上。<children>  标签把孩子结点加入到场景中,就像使用getChildren().add()方法。Image类加载了图片 fx_boxback.jpg,图片放在当前FXML文件路径下。 ImageView类用来显示图片。Label类有一个文本属性来自资源loginExample。在 FXML中,资源名称使用%来指定。加载时, FXML 加载器使用 loginExamplesetStyle()方法的CSS风格。 在Example 7  中 Label类使用style

另一种定义风格的方法和Java一样,加载样式表。使用样式表的话对于以后修改比较方便。看本文的 《使用样式表》  了解相关信息。



添加网格布局和控件

下面在边框布局的中部添加网格布局。使用网格布局可以在屏幕上垂直和水平的放置控件。见  Figure 5   .



Figure 5 Grid Pane in Center Region of Border Pane



Description of "Figure 5 Grid Pane in Center Region of Border Pane"   




Example 8  是网格布局的代码,放在 <center> 和  </center>


Example 8 Grid Layout with Controls



<GridPane alignment="top_center" hgap="8" vgap="8" 
            style="-fx-padding: 40 0 0 0">
            <children>
                <Label text="%signIn"
                    style="-fx-font: NORMAL 14 Tahoma;" 
                    GridPane.columnIndex="0" GridPane.rowIndex="0"/>
 
                <Label text="%userName"
                    GridPane.columnIndex="0" GridPane.rowIndex="1"
                    labelFor="$usernameField"/>
                <TextField fx:id="usernameField" prefColumnCount="10"
                    GridPane.columnIndex="1" GridPane.rowIndex="1"/>

                <Label text="%password"
                    GridPane.columnIndex="0" GridPane.rowIndex="2"
                    labelFor="$passwordField"/>
                <PasswordField fx:id="passwordField" prefColumnCount="10"
                    GridPane.columnIndex="1" GridPane.rowIndex="2"
                    onAction="#handlePasswordFieldAction"/>
 
                <Button fx:id="submitButton" text="%submit" 
                    GridPane.columnIndex="1" GridPane.rowIndex="3"
                    onAction="#handleSubmitButtonAction"/>
 
                <Label fx:id="buttonStatusText" 
                    GridPane.columnIndex="1" GridPane.rowIndex="4"
                    style="-fx-text-fill: #ff0000;"/>
            </children>
        </GridPane>


GridPane.columnIndex   和 GridPane.rowIndex属性对应GridPane类的setColumnIndex()   和setRowIndex()  方法。行列的值在网格开始时是0。比如,第一个Label控件的坐标是 (0,0)表示它在第一行第一列。或者说左上角。prefColumnCount 用于  TextField 和 PasswordField控件,来设置首选列宽是10fx:id属性会在文档的命名空间中创建一个变量,可以在后面引用。变量引用时使用  $来标识。在  Example 8  中,  TextField的ID是usernameField   .该ID分配给Label控件的labelFor属性, 为  TextField控件添加了一个label.PasswordField和Button控件有一个onAction属性。该符号引用一个定制化方法,会在后面创建FXMLExampleController类时创建。

尽管FXML创建界面很方便,但却不能实现行为。行为必须用Java代码实现(下一节)或者使用脚本,见 《使用脚本语言》.



添加按钮事件

现在创建控制器来管理按钮事件。本例演示如何把FXML和Java代码编写的事件控制器相关联。

1. Refactor--  Rename
2. Refactor
3.  打开FXMLExampleController.java删除代码,用下面的代替 Example 9   .
 Example 9 FXMLExampleController.javapackage fxmlexample;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
 
public class FXMLExampleController {
    @FXML private Label buttonStatusText;
    
    @FXML protected void handleSubmitButtonAction(ActionEvent event) {
        buttonStatusText.setText("Submit button pressed");
    }

    @FXML protected void handlePasswordFieldAction(ActionEvent event) {
        buttonStatusText.setText("Enter key pressed");
    }
}
@FXML注解用来标识非公开控制器成员和方法。

作为Java的补充,也可以使用其他编译型语言如Scala来实现控制器。.

现在可以运行了。输入内容点击Submit试试。

FXMLExample.zip  


使用脚本语言

除了使用Java创建控制器,可以使用任何提供JSR223-可编译脚本引擎的语言。比如 JavaScript, Groovy, Jython, 还有Clojure.下面是用 JavaScript编码FXML的。

  1. 在fxml_example.fxml文件中,添加JavaScript声明到XML声明后。
<?language javascript?>在
1. 
onAction="handleSubmitButtonAction(event);"PasswordField
1. 
onAction="handlePasswordFieldAction(event);"fx:controller
1.  从  
BorderPane标记中,在
1.   <script>中
添加js方法
1.  ,见Example 10   .
 
   
  
 Example 10 JavaScript in FXML



<BorderPane xmlns:fx="http:///fxml">
         <fx:script>
             function handleSubmitButtonAction() {
                 buttonStatusText.setText("Calling the JavaScript");
             }
             function handlePasswordFieldAction(event) {
                 buttonStatusText.text = "More JavaScript";
             }
         </fx:script>



当然也可以把js方法写进外部文件 (比如 fxml_example.js)然后像下面这样引用:

<fx:script source="fxml_example.js"/>

使用脚本编写FXML,调试时可能不会步入函数体。



使用样式表

使用内联样式,也可以添加样式表然后为结点设置关联。下面创建了样式表来定义网格布局和标签控件的样式。

  1. 创建样式表.
1. New -- Other
2. Other -- Cascading Style Sheet   , 然后  Next
3. Finish
4.  用下面的代码代替原来的代码 Example 11   . 
      
       
      
 Example 11 Contents of Style Sheet 
      @charset "utf-8";
/* 
    Document   : FXMLstylesheet.css
*/ 
 
.grid-pane {
  -fx-padding: 80 0 0 0;
}
 
.label {
    -fx-font: normal 36px Tahoma;
}

stage.show()

  1.   .scene.getStylesheets().add("fxmlexample/fxmlstylesheet.css");
  2. 打开 fxml_example.fxml添加样式类.
  1. 为<String>元素添加导入语句。

<?import java.lang.*?>

  1. 用下面代码代替GridPane Example 12   .
1.        
      
 Example 12 Style Class for Grid Pane 
      <GridPane alignment="top_center" hgap="8" vgap="8"> 
           <styleClass>
                <String fx:value="grid-pane"/>
            </styleClass>2.  用下面代码代替"Sign In" Label 。 Example 13   . 
      
       
      
 Example 13  Style class for Label 
      <Label text="%signIn"                    
    GridPane.columnIndex="0" GridPane.rowIndex="0">
         <styleClass>
               <String fx:value="label"/>
         </styleClass>
</Label>

<styleClass>

  1.  标签时,风格会应用到所有的类,除非它有自己的内联风格。所以Example 13 中的更改不仅应用到了 Sign In标签,也赋予了Username 和Password标签。但不会应用到Login Example标签,因为它有自己的内联样式,覆盖了其他风格。