将自定义FXML属性设置为自定义javafx组件的参数



我创建了自定义组件TableBlock。它由Label和TableView组成。例如,TableView可以有1到1000行。行数由FXML文件中的参数"rowsFromPrefs"定义。创建TableView需要此参数。TableView完全由JAva代码创建,在fxml中只是它的标记和参数,其中有许多行。

正如我所知,当JavaFX构造FXML组件时,它首先调用构造函数,然后调用@FXML注释字段,然后启动initialize()方法。

在我的例子中,当initialize()启动时,变量rowsFromPrefs仍然为null!但是,如果我试图从其他线程(而不是JavaFX启动器)获取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);
}
}

查看.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>

首先,请注意rowsFromPrefs上的@FXML注释没有任何作用。当当前对象为其控制器的FXML文件具有值与字段名称匹配的具有fx:id属性的元素时,@FXML导致为字段注入值。由于TableBlock.fxml没有包含fx:id="rowsFromPrefs"的元素,因此此注释没有任何作用。

当正在加载View.fxmlFXMLLoader遇到<TableBlock>元素时,它会通过调用其构造函数来创建一个TableBlock实例。然后,它将设置由属性指定的值。所以你的FXML元素

<TableBlock rowsFromPrefs="2" id="IDDQD"/>

本质上等同于

TableBlock tableBlock = new TableBlock();
tableBlock.setRowsFromPrefs("2");
tableBlock.setId("IDDQD");

当然,TableBlock的构造函数只是按照代码所说的去做:它创建一个FXMLLoader,为该FXMLLoader设置根和控制器,然后调用load()FXMLLoader加载过程将在控制器(构造函数正在执行的TableBlock对象)上设置@FXML注入的字段,然后调用initialize()

因此CCD_ 19被调用作为对CCD_ 21构造函数中的CCD_ 20的调用的一部分;当然,这一切都发生在调用CCD_ 22之前。

总之,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(...)方法。如果您希望为每个TableBlock实例固定rowsFromPrefs,我会使用上面描述的选项,并且只有当您希望能够在单个TableBlock实例的生命周期中更改rowsFromBlocks时,我才会使用第二个选项。

最新更新