如果我有一个像这样的自定义 JavaFX 组件(例如):
public class MenuWidget extends VBox implements Initializable {
@FXML
StackPane menus;
public MenuWidget() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println(menus.getChildren().size());
}
}
使用此 FXML:
<fx:root type="javafx.scene.layout.VBox" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1">
<StackPane fx:id="menus">
<padding>
<Insets top="5" left="5" bottom="5" right="5"></Insets>
</padding>
</StackPane>
</fx:root>
我在另一个 FXML 文件中使用这样的自定义组件:
<MenuWidget>
<menus>
<fx:include source="FirstMenu.fxml" />
<fx:include source="SecondMenu.fxml" />
</menus>
</MenuWidget>
为什么MenuWidget中的Initialize()方法打印0?本质上,我需要在构造 MenuWidget 时访问堆栈 Pane 的子级,以便我可以设置顶级菜单的其他菜单控件(我已从本示例中删除了它)。 FXMLLoader 不应该在调用 init 方法之前填充 Controller (MenuWidget)的所有属性吗?
编辑:发现 init 在构造函数完成之前被调用,因此尝试将 init 代码移动到构造函数中(在 fmxmlLoader.load() 调用之后),但它仍然不起作用。
最佳答案
MenuWidget
类及其关联的 FXML 文件是完全独立的。您没有在其中包含任何内容,也没有向 StackPane
添加任何子项。 。换句话说,这个:
public class MenuWidget extends VBox implements Initializable {
@FXML
StackPane menus;
public MenuWidget() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println(menus.getChildren().size());
}
}
加载此:
<fx:root type="javafx.scene.layout.VBox" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1">
<StackPane fx:id="menus">
<padding>
<Insets top="5" left="5" bottom="5" right="5"></Insets>
</padding>
</StackPane>
</fx:root>
一旦完成,initialize
方法被调用。没有任何内容添加到 menus
所以调用 menus.getChildren().size()
的结果当然是0
.
您正在其他地方加载此内容:
<MenuWidget>
<menus>
<fx:include source="FirstMenu.fxml" />
<fx:include source="SecondMenu.fxml" />
</menus>
</MenuWidget>
这会导致 MenuWidget
被实例化,这涉及调用 MenuWidget#initialize
方法,然后尝试将子项添加到 menus
。换句话说,如果这是有效且有效的 FXML,那么子项将添加到 MenuWidget
之后。实例已创建并初始化。
但是,<menus>
元素应该导致您的应用程序抛出异常。 MenuWidget
类没有定义 read-only list property命名menus
。如果你想使用<menus>
,并且您希望将该列表的元素添加到 menus
的子级中堆栈 Pane ,然后修改您的 MenuWidget
类(class):
public class MenuWidget extends VBox implements Initializable {
@FXML
StackPane menus;
public MenuWidget() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
System.out.println(menus.getChildren().size());
}
// add read-only list property (the "property" is read-only, not
// the list itself) named "menus"
public final ObservableList<Node> getMenus() {
return menus.getChildren();
}
}
但这在概念上似乎是错误的(至少对我来说)。我不太确定你想做什么,但也许你应该 fx:include
- 将其他 FXML 文件直接放入 MenuWidget
FXML 文件,而不是您当前正在执行的操作。这样您就可以将 Controller 和/或 View (请参阅 nested controllers )注入(inject) MenuWidget
中。类(class)。我也不确定是否使用 fx:root
根据您向我们展示的内容,在这种情况下是完全有保证的。继承自VBox
似乎没有给您的代码添加任何好处(即您没有添加任何功能) - 特别是因为您只向其中添加一个子项(然后向该子项添加子项)。也许标准的 FXML 文件 + Controller 会更合适。
关于java - 访问自定义 FXML 组件初始化中的子组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60106519/