我创建了自定义组件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>
首先,请注意,上面的@FXML
注释rowsFromPrefs
没有任何作用。@FXML
当当前对象为控制器的FXML文件具有一个fx:id
属性值与字段名称匹配的元素时,将导致为该字段注入值。由于TableBlock.fxml
没有元素fx:id="rowsFromPrefs"
,因此该注释没有任何作用。
当FXMLLoader
正在加载的View.fxml
遇到<TableBlock>
元素时,它将TableBlock
通过调用其构造函数来创建实例。然后它将设置属性指定的值。因此,您的FXML元素
<TableBlock rowsFromPrefs="2" id="IDDQD"/>
基本上等于
TableBlock tableBlock = new TableBlock();
tableBlock.setRowsFromPrefs("2");
tableBlock.setId("IDDQD");
当然,构造函数的作用TableBlock
只是执行代码说明的操作:它创建一个FXMLLoader
,设置根和控制器FXMLLoader
,然后调用load()
。用于负载过程的是 FXMLLoader
将设置@FXML
在控制器(该上-injected字段TableBlock
,其构造为执行的对象),然后调用initialize()
。
因此,在构造函数中,initialize()
作为调用的一部分被调用。当然,这一切都是在调用之前发生的。FXMLLoader.load()
TableBlock
setRowsFromPrefs("2");
因此,总而言之,在解析TableBlock.initialize()
之后调用TableBlock.fxml
,并将其中定义的任何元素注入到其相应的带@FXML
注释的字段中,但这View.fxml
是在加载之前发生的。
解决此问题的一种方法是传递rowsFromPrefs
给TableBlock
构造函数。为此,请使用@NamedArg
注释:
public class TableBlock extends VBox{
private final String rowsFromPrefs;
@FXML
private Label label;
public TableBlock(@NamedArg("rowsFromPrefs") String rowsFromPrefs) {
this.rowsFromPrefs = rowsFromPrefs ;
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;
}
}
现在,FXML中的属性将传递给构造函数,而不是set方法,因此rowsFromPrefs
将在调用之前fxmlLoader.load()
根据需要进行初始化。
当然,另一个选择只是将代码从initialize()
方法移到setRowsFromPrefs(...)
方法。如果打算rowsFromPrefs
针对每个TableBlock
实例进行修复,则将使用上述选项,并且仅当您希望能够rowsFromBlocks
在单个TableBlock
实例的生命周期内进行更改时,才使用第二个选项。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句