我创建了自定义组件TableBlock . 它由Label和TableView组成 . 例如,TableView可以有1到1000行 . 行数由FXML文件中的参数“rowsFromPrefs”定义 . 创建TableView需要此参数 . TableView完全由JAva代码创建,fxml只是它的标签和带有多行的参数 .
据我所知,当JavaFX构造FXML组件时,它首先调用构造函数,然后调用@FXML注释字段,然后启动initialize()方法 .
在我的情况下,当initialize()开始时,变量rowsFromPrefs仍为null!但是,如果我试图从其他线程(而不是JavaFX-launcher)获取rowsFromPrefs的值,我看到它定义=“2”就像它应该的那样 .
所以我无法理解Java在什么时候从FXML文件中分配对象参数 . 如何在创建时将参数从fxml文件传递给对象 .
我看到了构造函数参数的@NamedArg注释 . 它是创建对象时传递参数的唯一方法吗?
控制器可以定义一个initialize()方法,当相关文档的内容已经完全加载时,该方法将在实现控制器上调用一次:
TableBlock.java
public class TableBlock extends VBox{
@FXML
private String rowsFromPrefs;
@FXML
private Label label;
public TableBlock() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TableBlock.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
@FXML
public void initialize() {
this.table = createTable(rowsFromPrefs);
}
public String getRowsFromPrefs() {
System.out.println("getRowsFromPrefs");
return rowsFromPrefs;
}
public void setRowsFromPrefs(String rowsFromPrefs) {
this.rowsFromPrefs = rowsFromPrefs;
}
}
TableBlock.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import ru.laz.model.controls.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import ru.laz.model.controls.tableblock.*?>
<fx:root type="javafx.scene.layout.VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
</children>
</fx:root>
View.java
public class View extends Application {
Parent root = null;
private Scene scene;
@Override
public void init() {
try {
root = FXMLLoader.load(getClass().getResource("View.fxml"));
root.requestLayout();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void start(final Stage stage) throws Exception {
scene = new Scene(root, 640, 480, Color.LIGHTGRAY);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
View.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import ru.laz.model.controls.tableblock.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TableBlock rowsFromPrefs="2" id="IDDQD"/>
</children>
</AnchorPane>
1 回答
首先,请注意
rowsFromPrefs
上的@FXML
注释没有用处 . 当当前对象为控制器的FXML文件具有fx:id
属性且其值与字段名称匹配的元素时,@FXML
会导致为该字段注入值 . 由于TableBlock.fxml
没有fx:id="rowsFromPrefs"
的元素,因此该注释没有做任何事情 .当正在加载
View.fxml
的FXMLLoader
遇到<TableBlock>
元素时,它会通过调用其构造函数创建一个TableBlock
实例 . 然后它将设置属性指定的值 . 所以你的FXML元素基本上相当于
当然,
TableBlock
的构造函数只是执行代码所要做的事情:它创建FXMLLoader
,为FXMLLoader
设置根和控制器,然后调用load()
. 该FXMLLoader
的加载过程将设置控制器上的@FXML
-injected字段(构造函数正在执行的TableBlock
对象),然后调用initialize()
.因此
initialize()
作为TableBlock
构造函数中FXMLLoader.load()
调用的一部分被调用;当然这一切都发生在setRowsFromPrefs("2");
被调用之前 .总而言之,在
TableBlock.fxml
被解析之后调用了TableBlock.initialize()
,并且在那里定义的任何元素都被注入到它们对应的@FXML
注释字段中,但这是在加载View.fxml
之前发生的 .解决此问题的一种方法是将
rowsFromPrefs
传递给TableBlock
构造函数 . 为此,请使用@NamedArg annotation:现在,FXML中的属性将传递给构造函数而不是set方法,因此
rowsFromPrefs
将在您根据需要调用fxmlLoader.load()
之前进行初始化 .当然,另一个选项只是将代码从
initialize()
方法移动到setRowsFromPrefs(...)
方法 . 如果您希望为每个TableBlock
实例修复rowsFromPrefs
,我将使用上述选项,并且仅当您希望能够在单个TableBlock
实例的生命周期内更改rowsFromBlocks
时才使用第二个选项 .