简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM