[英]Set custom FXML properties as parameters for custom javafx component
I created custom component TableBlock. 我创建了自定义组件TableBlock。 It consists of a Label and TableView. 它由Label和TableView组成。 TableViewcan have, for example, from 1 to 1000 rows. TableView可以具有例如1到1000行。 Number of rows is defined by parameter "rowsFromPrefs" in FXML file. 行数由FXML文件中的参数“ rowsFromPrefs”定义。 This parameter is needed for creation of TableView. 创建TableView时需要此参数。 TableView is fully created by JAva code, in fxml is just its tag and parameter with a number of rows. TableView是完全由JAva代码创建的,在fxml中只是其标记和带有多行的参数。
As i know, when JavaFX constructs FXML component, it first calls constructor, then @FXML annotated fields, then starts initialize() method. 据我所知,当JavaFX构造FXML组件时,它首先调用构造函数,然后调用@FXML带注释的字段,然后启动initialize()方法。
In my case when initialize() starts, variable rowsFromPrefs still is null! 在我的情况下,当initialize()启动时,变量rowsFromPrefs仍然为null! But, if i try to get the value of rowsFromPrefs from other thread (not JavaFX-launcher), i see that it defined = "2" like it should be. 但是,如果我尝试从其他线程(而不是JavaFX-launcher)中获取rowsFromPrefs的值,我会看到它像应该那样定义为“ 2”。
So i can`t understand at what moment Java assigns object paramters from FXML file. 所以我不明白Java在什么时候从FXML文件分配对象参数。 How can i pass parameter from fxml file to object when it is being created. 创建参数时,如何将参数从fxml文件传递给对象。
I saw @NamedArg annotation for constructor parameters. 我看到了@NamedArg注释的构造函数参数。 Is it the only one way of passing parameter when objects are creating? 它是创建对象时传递参数的唯一方法吗?
the controller can define an initialize() method, which will be called once on >an implementing controller when the contents of its associated document have >been completely loaded: 控制器可以定义一个initialize()方法,当其关联文档的内容已完全加载时,将在实现控制器上调用一次:
TableBlock.java 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 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 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 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>
First, note that the @FXML
annotation on rowsFromPrefs
is serving no purpose. 首先,注意@FXML
上标注rowsFromPrefs
是服务没有任何意义。 @FXML
causes a value to be injected for the field when the FXML file for which the current object is the controller has an element with an fx:id
attribute whose value matches the field name. @FXML
使待注射的值的字段时的量,当前对象是控制器的FXML文件具有与一个元件fx:id
属性,它的值的字段名称相匹配。 Since TableBlock.fxml
has no element with fx:id="rowsFromPrefs"
, this annotation isn't doing anything. 由于TableBlock.fxml
没有带有fx:id="rowsFromPrefs"
元素,因此此注释不执行任何操作。
When the FXMLLoader
that is loading View.fxml
encounters the <TableBlock>
element, it creates a TableBlock
instance by calling its constructor. 当FXMLLoader
正在加载View.fxml
遇到<TableBlock>
元素,它创建一个TableBlock
通过调用它的构造实例。 Then it will set the values specified by the attributes. 然后它将设置属性指定的值。 So your FXML element 因此,您的FXML元素
<TableBlock rowsFromPrefs="2" id="IDDQD"/>
is essentially equivalent to 基本上等于
TableBlock tableBlock = new TableBlock();
tableBlock.setRowsFromPrefs("2");
tableBlock.setId("IDDQD");
Of course, the constructor for TableBlock
just does what the code says to do: it creates a FXMLLoader
, sets the root and controller for that FXMLLoader
, and then calls load()
. 当然,对于构造TableBlock
少了点什么码说的事:它创造了FXMLLoader
,设置根和控制器为FXMLLoader
,然后调用load()
The load process for that FXMLLoader
will set the @FXML
-injected fields on the controller (the TableBlock
object whose constructor is executing), and then call initialize()
. 该加载过程FXMLLoader
将设置@FXML
-injected控制器(在上字段TableBlock
对象,其构造为执行),然后调用initialize()
So initialize()
is invoked as part of the call to FXMLLoader.load()
that is in the TableBlock
constructor; 因此,在对TableBlock
构造函数中的TableBlock
initialize()
进行调用的过程中,调用了FXMLLoader.load()
。 of course this all happens before setRowsFromPrefs("2");
当然,这一切都发生在setRowsFromPrefs("2");
之前setRowsFromPrefs("2");
is invoked. 被调用。
So in summary, TableBlock.initialize()
is called after TableBlock.fxml
has been parsed, and any elements defined there injected into their corresponding @FXML
-annotated fields, but this happens before View.fxml
has been loaded. 因此,总而言之,在解析TableBlock.fxml
之后,将调用TableBlock.initialize()
,并将其中定义的任何元素注入其相应的@FXML
字段中,但这是在加载View.fxml
之前发生的。
One way to fix this is to pass rowsFromPrefs
to the TableBlock
constructor. 解决此问题的一种方法是将rowsFromPrefs
传递给TableBlock
构造函数。 To do this, use the @NamedArg
annotation : 为此,请使用@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;
}
}
Now your attribute in the FXML will be passed to the constructor instead of to a set method, so rowsFromPrefs
will be initialized before you call fxmlLoader.load()
, as required. 现在,您的FXML中的属性将传递给构造函数,而不是传递给set方法,因此在需要调用fxmlLoader.load()
之前, rowsFromPrefs
进行初始化。
The other option, of course, would simply be to move the code from the initialize()
method to the setRowsFromPrefs(...)
method. 当然,另一个选择就是将代码从initialize()
方法移到setRowsFromPrefs(...)
方法。 I would use the option described above if you intend rowsFromPrefs
to be fixed for each TableBlock
instance, and use the second option only if you want to be able to change rowsFromBlocks
during the lifecycle of an individual TableBlock
instance. 如果您打算为每个TableBlock
实例固定rowsFromPrefs
,那么我将使用上述选项,并且仅当您希望能够在单个TableBlock
实例的生命周期内更改rowsFromBlocks
时,才使用第二个选项。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.