JavaFX 加载 fxml 文件

JavaFX 加载 fxml 文件主要有两种方式,第一种方式通过 FXMLLoader 类直接加载 fxml 文件,简单直接,但是有些控件目前还不知道该如何获取,所以只能显示,目前无法处理。第二种方式较为复杂,但是可以使用与 fxml 文件对应的 ***Controller 类可以操作 fxml 文件中的所有控件。现将两种方式介绍如下:

方式一:

  1. 创建 fxml 的 UI 文件
  2. 将 fxml 文件放置到对应位置
  3. FXMLLoader 加载文件显示

fxml 文件:











   
      

UI 显示:

JavaFX 加载 fxml 文件_第1张图片

java加载 fxml 文件:

package ch04;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Cursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;

/**
 * @author qiaowei
 * @version 1.0
 * @package ch04
 * @date 2020/5/24
 * @description
 */
public class LoadFXMLTest extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage)  {
        try {
            useFXMLLoader(primaryStage);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    /**
      @class      LoadFXMLTest
      @date       2020/5/24
      @author     qiaowei
      @version    1.0
      @brief      使用FXMLLoader加载fxml文件并生成相应的java类
      @param      primaryStage Application创建的Stage实例
     */
    public void useFXMLLoader(Stage primaryStage) throws Exception {
        Parent root =
                FXMLLoader.load(getClass().getResource("/sources/StageModalityWindow.fxml"));

        // 根据控件在窗体控件的添加顺序编号获取
        // Button ownedNone = (Button) ((VBox) root).getChildren().get(0);
        Button ownedNone = (Button) root.lookup("#ownedNone");
        ownedNone.setOnAction(e -> resetWindowTitle(primaryStage, "hello ownedNone"));

        Button ownedWindowModal = (Button) root.lookup("#ownedWindowModal");
        ownedWindowModal.setOnAction(e -> resetWindowTitle(primaryStage, "ownedWindowModal"));

        int width = ((int) ((VBox) root).getPrefWidth());
        int height = ((int) ((VBox) root).getPrefHeight());
//
        Scene scene = new Scene(root, width, height);
//        Scene scene = new Scene(root);
//        Button ownedNoneButton = ((VBox) root).getChildren().get(1);

        primaryStage.setTitle("StageModality");
        primaryStage.setScene(scene);

        primaryStage.show();
    }

    /**
     @class        StageModalityApp
     @date         2020/3/6
     @author       qiaowei
     @version      1.0
     @description  根据窗口拥有者和模式设置窗口状态
     @param        owner 窗口的父控件
     @param        modality 窗口的模式
     */
    private void showDialog(Window owner, Modality modality) {
        // Create a new stage with specified owner and modality
        Stage stage = new Stage();
        stage.initOwner(owner);
        stage.initModality(modality);
        Label modalityLabel = new Label(modality.toString());
        Button closeButton = new Button("Close");
        closeButton.setOnAction(e -> stage.close());

        VBox root = new VBox();
        root.getChildren().addAll(modalityLabel, closeButton);
        Scene scene = new Scene(root, 200, 100);

        // 设置鼠标在scene的显示模式
        scene.setCursor(Cursor.HAND);

        stage.setScene(scene);
        stage.setTitle("A Dialog Box");
        stage.show();
    }

    private void resetWindowTitle(Stage stage, String title) {
        stage.setTitle(title);
    }
}

根据 fxml 中控件的 “fx:id” 属性获取控件,并添加触发事件。通过 button 修改窗体的 title

注意两点:

  1. fxml 的存放路径,不同位置加载 String 不同。
  2. fxml 加载后返回的是 root 实例,需要放置到 sence 实例中再显示。
  3. 在通过 fxml 中控件的 id 返回控件时,id 前要加 #字符。

第二种方式

  1. 创建 fxml 文件,在 fxml 文件的顶层控件设置 “fx:controller”,属性值 =“完整的包路径.***Controller”

JavaFX 加载 fxml 文件_第2张图片

在完整的包路径下创建 ***Controller 类,在 fxml 文件中定义的控件和方法前都加 “@FXML”,注意两个文件中的对应控件、方法名称必须保持一致。

注意:***Controller 类在这里只继承 Objec,这样其中的控件都自动绑定到 fxml 中的控件,不会出现控件为 null 的情况。退出程序按钮。不添加 "@FXML" 注解,系统认为时类自己添加的控件,不会与 fxml 文件中同名控件绑定,系统不会自动初始化,值为 null;添加 "@FXML" 注解,系统会自动绑定到 fxml 文件中的同名控件,会自动给初始化为 MenuItem 的实例。注意字段与方法的区别,如果在 fxml 中定义方法,在 java 文件中必须有同名的方法,而且方法前要加 "@FXML" 注释。

JavaFX 加载 fxml 文件_第3张图片

 

JavaFX 加载 fxml 文件_第4张图片

示例如下:创建一个有 menuBar、menuItem 的窗体,在 fxml 中定义 menuItem 的 id 和 Action,在 ***Controller 中操作控件 menuItem 和 Action。

fmxl 文件,其中 openItem 没有设置 onAction,closeItem 设置 onAction,注意之后的两种处理方式。







   
      
        
          
            
                
                
            
          
          
            
              
            
          
          
            
              
            
          
        
      
      
   

 对应的 ***Controller 类,openItem 通过在 setStage 方法中绑定 setOnAction(不能在构造函数中进行绑定,只能通过其它方法绑定!!!),closeItem 通过 fxml 文件中的设置直接绑定了 exitApp 方法(注:两个 MenuItem 控件通过不同的方法进行动作绑定,一个在 fxml 文件中绑定,一个在类文件中绑定)

注意:fxml 文件中设置的控件、方法在 ***Controller 类中要通过 “@FXML” 标识方法、字段在绑定,且方法、字段名称完全一致。

package ch06;

import javafx.fxml.FXML;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;

/**
  @package      ch06 
  @file         VHFFrameController.java
  @date         2020/5/27
  @author       qiaowei
  @version      1.0
  @description  与VHFFFrame.fxml文件对应的Controller类
 */
public class VHFFrameController {
    
    public VHFFrameController() {
        operator = Operator.instance();
    }
    
    /**
      @class      VHFFrameController
      @date       2020/5/27
      @author     qiaowei
      @version    1.0
      @brief      打开文件选择窗口
     */
//    @FXML
    public void openChooser() {
        if (isStage()) {
            operator.openFile(primaryStage);       
        }
    }
    
    /**
      @class      VHFFrameController
      @date       2020/5/27
      @author     qiaowei
      @version    1.0
      @brief      设置primaryStage字段实例,当字段已经设置过,跳过设置步骤
      @param      primaryStage 从主程序传来的主primaryStage实例
     */
    public void setStage(Stage primaryStage) {
        if ( !isStage()) {
            this.primaryStage = primaryStage;
        }

        openItem.setOnAction(e -> openChooser());
    }
    
    /**
      @class      VHFFrameController
      @date       2020/5/27
      @author     qiaowei
      @version    1.0
      @brief      退出程序
     */
    @FXML
    private void exitApp() {
        operator.exitApp();
    }
    
    /**
      @class      VHFFrameController
      @date       2020/5/27
      @author     qiaowei
      @version    1.0
      @brief      判断primaryStage实例是否为null,为null时,返回false;反之返回true;
      @return     true primaryStage不为null;反之返回false
     */
    private boolean isStage() {
        boolean flag = false;
        if (null != primaryStage) {
            flag = true;
        } else {
            flag = false;
        }
        
        return flag;
    }
    
    @FXML
    private MenuItem openItem;
    
    @FXML
    private MenuItem closeItem;
    
    @FXML
    private MenuBar menuBar;

    private static Operator operator;
    
    private Stage primaryStage;
}

 具体操作类 Operator,实现退出程序,打开 FileChooser 窗体两个功能:

package ch06;

import javafx.application.Platform;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;

/**
 * @author qiaowei
 * @version 1.0
 * @package ch06
 * @date 2020/5/27
 * @description
 */
public class Operator {
    
    private Operator() {
        
    }
    
    public static Operator instance() {
        if (null == OPERATOR) {
            OPERATOR = new Operator();
        }
        
        return OPERATOR;
    }
    
    public void openFile(Stage stage) {
        FileChooser chooser = new FileChooser();

        File file = chooser.showOpenDialog(stage);
    }
    
    public void exitApp() {
        Platform.exit();
    }
    
    private static Operator OPERATOR = null;
}

JavaFX 运行类:

package ch06;

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

/**
 * @author qiaowei
 * @version 1.0
 * @package ch06
 * @date 2020/5/27
 * @description
 */
public class VHFApp extends Application {

    public static void main(String[] args) {
        try {
            Application.launch(VHFApp.class, args);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader fxmlLoader = 
                new FXMLLoader(getClass().getResource("/sources/VHFFrame.fxml"));
        Parent root = fxmlLoader.load();
//        Parent root = 
//                FXMLLoader.load(getClass().getResource("/sources/VHFFrame.fxml"));

        int width = ((int) ((AnchorPane) root).getPrefWidth());
        int height = ((int) ((AnchorPane) root).getPrefHeight());

        Scene scene = new Scene(root, width, height);

        primaryStage.setTitle("VHFApplication");
        primaryStage.setScene(scene);
        
        VHFFrameController controller = fxmlLoader.getController();
        controller.setStage(primaryStage);

        primaryStage.show();
    }
    
//    private static VHFFrameController controller = new VHFFrameController();
}

实现如下:

JavaFX 加载 fxml 文件_第5张图片

示例 2:

文件树图:

JavaFX 加载 fxml 文件_第6张图片

 fxml 文件:指定对应的 Controller 文件,对控件 exitItem 制定了对应的方法 exitApplication。