除了使用Java原生的Code產生圖形介面外,JavaFX還支援使用XML來描述圖形介面(這好像已經成為一種趨勢了)。甚至Oracle還提供了一種叫作JavaFX Scene Builder的工具,讓開發人員製作圖形介面完全不用寫到程式。



JavaFX Scene Builder

JavaFX Scene Builder可以到Oracle的網站上下載安裝,之後就可以拿來排介面了!

拿之前登入表單的例子,使用JavaFX Scene Builder花不到五分鐘就可以做好下面的介面!之後存檔成fxml檔案就可以套入在JavaFX內啦!

使用FXML開發JavaFX的專案要注意到,若程式需要存取到圖形元件的狀態,需設定ID欄位,這樣才可以使用node的lookup或是lookupAll方法,以CSS名稱(#ID名稱)的字串方式,來找出該node底下符合此ID名稱的sub node。如果圖形元件需要處理事件,可以設定onAction欄位,由FXML去指向事件觸發後要執行的方法名稱,但此方法需要用到@FXML這個annotation來實作。另外,@FXML也可以抓到FXML元件的fxID。要使用@FXML annotation的話,必須要替FXML指定Controller(指到JavaFX應用程式內的某個類別)。

套入FXML

JavaFX提供FXMLLoader.load()方法,可以將FXML讀入,並轉換成Node。

撰寫程式

LoginJavaFX.java

package javafxlogin;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class LoginJavaFX extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("Login.fxml"));

        //用讀進來FXML的作為Scene的root node
        Scene scene = new Scene(root, 300, 275);
        primaryStage.setScene(scene);

        //顯示Stage
        primaryStage.setTitle("登入");
        primaryStage.show();
    }
}

FXController.java

package javafxlogin;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.text.Text;

public class FXController {

    @FXML
    private Text actiontarget;

    @FXML
    protected void submit(ActionEvent event) {
        actiontarget.setText("登入成功");
    }
}

Login.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="275.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxlogin.FXController">
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
    </rowConstraints>
    <children>
        <Text strokeType="OUTSIDE" strokeWidth="0.0" text="歡迎">
            <font>
                <Font size="20.0"/>
            </font>
        </Text>
        <Label text="帳號:" GridPane.rowIndex="1"/>
        <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/>
        <Label text="密碼:" GridPane.rowIndex="2"/>
        <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="2"/>
        <HBox alignment="BOTTOM_RIGHT" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4">
            <children>
                <Button mnemonicParsing="false" onAction="#submit" text="登入"/>
            </children>
        </HBox>
        <Text fx:id="actiontarget" id="actiontarget" fill="#ff4529" strokeType="OUTSIDE" strokeWidth="0.0" GridPane.columnIndex="1" GridPane.rowIndex="6"/>
    </children>
    <padding>
        <Insets bottom="25.0" left="25.0" right="25.0" top="25.0"/>
    </padding>
</GridPane>

執行結果: