JavaFX 加载 fxml 文件主要有两种方式,第一种方式通过 FXMLLoader 类直接加载 fxml 文件,简单直接,但是有些控件目前还不知道该如何获取,所以只能显示,目前无法处理。第二种方式较为复杂,但是可以使用与 fxml 文件对应的 ***Controller 类可以操作 fxml 文件中的所有控件。现将两种方式介绍如下:
fxml 文件:
UI 显示:
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
注意两点:
在完整的包路径下创建 ***Controller 类,在 fxml 文件中定义的控件和方法前都加 “@FXML”,注意两个文件中的对应控件、方法名称必须保持一致。
示例如下:创建一个有 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();
}
实现如下:
示例 2:
文件树图:
fxml 文件:指定对应的 Controller 文件,对控件 exitItem 制定了对应的方法 exitApplication。
package ui;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.MenuItem;
import java.net.URL;
import java.util.ResourceBundle;
/**************************************************************************************************
* @copyright 2003-2022
* @package ui
* @file MainWindowController.java
* @date 2022/5/2 22:54
* @author qiao wei
* @version 1.0
* @brief MainWindow.fxml的绑定类。
* @history
**************************************************************************************************/
public class MainWindowController {
public MainWindowController() {
setupAttributes();
}
@FXML
public void initialize() {
if (null != openItem) {
openItem.setOnAction(e -> exitApplication());
}
}
public void setupAttributes() {
if (null != openItem) {
openItem.setOnAction(e -> openFile());
}
}
private void openFile() {
}
@FXML
private void exitApplication() {
if (null != openItem) {
System.out.println("测试@FXML注释作用");
}
Platform.exit();
}
@FXML
private MenuItem openItem;
/**********************************************************************************************
* @date 2022/5/2 22:48
* @author qiao wei
* @brief 退出程序按钮。不添加"@FXML"注解,系统认为时类自己添加的控件,不会与fxml文件中同名控件绑定,系统
* 不会自动初始化,值为null;添加"@FXML"注解,系统会自动绑定到fxml文件中的同名控件,会自动给初始化
* 为MenuItem的实例。注意字段与方法的区别,如果在fxml中定义方法,在java文件中必须有同名的方法,而且
* 方法前要加"@FXML"注释。
**********************************************************************************************/
@FXML
private MenuItem exitItem;
}
启动类:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import ui.MainWindowController;
/**************************************************************************************************
@Copyright 2003-2022
@package PACKAGE_NAME
@date 2022/5/2
@author qiao wei
@version 1.0
@brief TODO
@history
*************************************************************************************************/
public class Main extends Application {
public static void main(String[] args) {
try {
Application.launch(Main.class, args);
} catch (Exception exception) {
exception.printStackTrace();
}
}
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader =
new FXMLLoader(getClass().getResource("/fxml/MainWindow.fxml"));
Parent root = fxmlLoader.load();
// MainWindowController controller = fxmlLoader.getController();
Rectangle2D rectangle2D = Screen.getPrimary().getBounds();
// 获取窗体左上角初始坐标
double xPosition = rectangle2D.getWidth() / 5;
double yPosition = rectangle2D.getHeight() / 5;
// 获取窗体尺寸
int width = (int) rectangle2D.getWidth() * 2 / 3;
int height = (int) rectangle2D.getHeight() * 2 / 3;
// 设置窗体起始坐标、尺寸
primaryStage.setScene(new Scene(root, width, height));
primaryStage.setX(xPosition);
primaryStage.setY(yPosition);
primaryStage.setTitle("Radar Track Application");
primaryStage.show();
}
}
运行结果: