前面介紹了JavaFX的常見控制項用法,雖然JavaFX控制項比起AWT與Swing要好用些,但是一樣通過代碼編寫控制項界面,並沒有提高什麼開發效率。要想瀏覽界面的展示效果,都必須運行測試程式才能觀看,即使只是微調控制項的大小,也得重新運行程式查看效果,顯然既費時又費力。為此JavaFX提供了另一種給界面排 ...
前面介紹了JavaFX的常見控制項用法,雖然JavaFX控制項比起AWT與Swing要好用些,但是一樣通過代碼編寫控制項界面,並沒有提高什麼開發效率。要想瀏覽界面的展示效果,都必須運行測試程式才能觀看,即使只是微調控制項的大小,也得重新運行程式查看效果,顯然既費時又費力。為此JavaFX提供了另一種給界面排版的方式,不必使用Java代碼堆砌控制項,而是利用FXML文件開展界面佈局,同時藉助於idea的預覽功能,無需運行程式即可直接觀察FXML的佈局效果。所謂“FXML”意思是JavaFX專用的XML格式,它基於XML標準並加以擴展,每個JavaFX控制項均有對應的XML標簽,把這些蘊含控制項的標簽組裝起來,便形成了一個視窗界面專屬的佈局文件。
舉個簡單的例子,現在準備畫登錄界面,包含用戶名輸入框、密碼輸入框,以及登錄按鈕,這些控制項自上往下分成三行排列。該界面預期的展示效果如下圖所示。
倘若完全使用代碼實現以上的登錄界面,無疑要反覆地調整代碼並多次執行程式,才能達到滿意的佈局效果。那麼採用FXML方式的話,可以把與界面相關的控制項元素剝離出來,改為在FXML文件中書寫XML標簽結構。比如上述登錄頁對應的FXML文件名叫login.fxml,其內容示例如下:
<?import javafx.scene.layout.FlowPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.control.PasswordField?> <FlowPane xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="5" vgap="5"> <HBox fx:id="hbUser" prefWidth="400" prefHeight="40"> <Label fx:id="labelUser" prefWidth="120" prefHeight="40" text="用戶名:" /> <TextField fx:id="fieldUser" prefWidth="280" prefHeight="40" /> </HBox> <HBox fx:id="hbPassword" prefWidth="400" prefHeight="40"> <Label fx:id="labelPassword" prefWidth="120" prefHeight="40" text="密 碼:" /> <PasswordField fx:id="fieldPassword" prefWidth="280" prefHeight="40" /> </HBox> <Button fx:id="btnLogin" prefWidth="400" prefHeight="40" text="登 錄" /> </FlowPane>
在idea中打開login.fxml,註意到該文件界面的左下角有兩個選項卡,一個叫“Text”,另一個叫“Scene Builder”。當前打開的login.fxml展示為文本內容,對應的正是“Text”選項,此時單擊右邊的“Scene Builder”選項,原先的文本內容迅速變為一組可視化頁面,頁面中央呈現著login.fxml的預覽效果,正如下圖所示。
原來fxml文件類似於html文件,儘管html文件內部充斥著各種文本標簽,但使用瀏覽器打開html文件總能看到排版精美的網頁;而idea自帶的Scene Builder承擔了fxml瀏覽器的角色,只要程式員修改了fxml文件的格式內容,切換至“Scene Builder”選項就能立刻看見修改後的界面效果,比起傳統的運行程式看效果的方式,Scene Builder的渲染速度要快得多。
回頭再看前述的login.fxml,它的文件內容分為兩大塊,前面一塊形如“<?import ***?>”,其作用是導入指定包名路徑的控制項,與Java代碼的import語句相似;後面一塊包含各級控制項的嵌套結構,其標簽格式為“<控制項名稱 屬性列表></控制項名稱>”,如果當前控制項不存在下級控制項,則它的標簽格式可簡化為“<控制項名稱 屬性列表 />”。依據login.fxml的標簽內容,可知該界面採取FlowPane流式窗格,且流式窗格擁有下列三類控制項:
1、容納用戶名組件的水平箱子HBox,它的編號是hbUser。該箱子內部又有編號為labelUser的用戶名標簽,以及編號為fieldUser的用戶名輸入框;
2、容納密碼組件的水平箱子HBox,它的標識為hbPassword。該箱子內部又有編號為labelPassword的密碼標簽,以及編號為fieldPassword的密碼輸入框;
3、編號為btnLogin的登錄按鈕;
引入FXML佈局之後,Java代碼要改為從指定的fxml文件中載入界面,也就是將場景的創建過程改成如下兩行代碼:
// 從FXML資源文件中載入程式的初始界面 Parent root = FXMLLoader.load(getClass().getResource("login.fxml")); Scene scene = new Scene(root, 410, 240); // 創建一個場景
於是繪製界面的JavaFX代碼縮小到了下麵寥寥幾行:
//登錄視窗的程式入口(FXML佈局控制項) public class LoginMain extends Application { @Override public void start(Stage stage) throws Exception { // 應用程式開始運行 stage.setTitle("登錄視窗"); // 設置舞臺的標題 // 從FXML資源文件中載入程式的初始界面 Parent root = FXMLLoader.load(getClass().getResource("login.fxml")); Scene scene = new Scene(root, 410, 240); // 創建一個場景 stage.setScene(scene); // 設置舞臺的場景 stage.setResizable(true); // 設置舞臺的尺寸是否允許變化 stage.show(); // 顯示舞臺 } public static void main(String[] args) { launch(args); // 啟動JavaFX應用,接下來會跳到start方法 } }
接著運行上面的LoginMain程式,彈出的登錄界面正如預期所示。
JavaFX的絕大多數靜態控制項,都能以單個標簽的形式添加到fxml之中,除了前面例子提及的流式窗格FlowPane、水平箱子HBox、按鈕Button、標簽Label、文本輸入框TextField、密碼輸入框PasswordField,還包括網格窗格GridPane、邊界窗格BorderPane、垂直箱子VBox、多行輸入框TextArea、覆選框CheckBox、下拉框ComboBox等。然而單選按鈕RadioButton的添加方式別具一格,緣由在於好幾個單選按鈕要構成一個按鈕小組,這樣才能讓同組的單選按鈕聯動起來。因此fxml得先聲明一個ToggleGroup標簽,並給它分配標簽編號,然後在RadioButton標簽後面添加toggleGroup屬性,指定加入前一步的ToggleGroup編號。操作單選按鈕的具體fxml片段示例如下:
<HBox fx:id="hbType" prefWidth="400" prefHeight="40"> <Label fx:id="labelType" prefWidth="120" prefHeight="40" text="登錄類型:" /> <fx:define> <ToggleGroup fx:id="tgType" /> </fx:define> <RadioButton fx:id="rbPassword" prefWidth="140" prefHeight="40" toggleGroup="$tgType" text="密碼登錄" selected="true" /> <RadioButton fx:id="rbVerifycode" prefWidth="140" prefHeight="40" toggleGroup="$tgType" text="驗證碼登錄" /> </HBox>
當然,新來的RadioButton和ToggleGroup也要在fxml頭部添加以下的導入語句:
<?import javafx.scene.control.RadioButton?> <?import javafx.scene.control.ToggleGroup?>
把與單選按鈕有關的xml標簽補充到login.fxml,再另存為新的fxml文件名叫login_with_flow.fxml,完整的文件內容如下所示:
<?import javafx.scene.layout.FlowPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.control.PasswordField?> <?import javafx.scene.control.RadioButton?> <?import javafx.scene.control.ToggleGroup?> <FlowPane xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="5" vgap="5"> <HBox fx:id="hbType" prefWidth="400" prefHeight="40"> <Label fx:id="labelType" prefWidth="120" prefHeight="40" text="登錄類型:" /> <fx:define> <ToggleGroup fx:id="tgType" /> </fx:define> <RadioButton fx:id="rbPassword" prefWidth="140" prefHeight="40" toggleGroup="$tgType" text="密碼登錄" selected="true" /> <RadioButton fx:id="rbVerifycode" prefWidth="140" prefHeight="40" toggleGroup="$tgType" text="驗證碼登錄" /> </HBox> <HBox fx:id="hbUser" prefWidth="400" prefHeight="40"> <Label fx:id="labelUser" prefWidth="120" prefHeight="40" text="用戶名:" /> <TextField fx:id="fieldUser" prefWidth="280" prefHeight="40" /> </HBox> <HBox fx:id="hbPassword" prefWidth="400" prefHeight="40"> <Label fx:id="labelPassword" prefWidth="120" prefHeight="40" text="密 碼:" /> <PasswordField fx:id="fieldPassword" prefWidth="280" prefHeight="40" /> </HBox> <Button fx:id="btnLogin" prefWidth="400" prefHeight="40" text="登 錄" /> <Label fx:id="labelLoginResult" prefWidth="400" prefHeight="40" text="這裡顯示登錄結果" /> </FlowPane>
然後利用Scene Builder觀察login_with_flow.fxml的預覽界面,由下圖可見單選按鈕組合已經添加至登錄視窗的上方。
接著修改LoginMain.java,將創建場景時載入的資源文件名改為login_with_flow.fxml,再重新運行測試程式,此時彈出的登錄界面如下圖所示:
比較Scene Builder的預覽界面,以及實際運行的視窗界面,總體而言兩者大同小異,基本的佈局排列是吻合的。
更多Java技術文章參見《Java開發筆記(序)章節目錄》