JavaFX - 将 FXML 包装到 Java 类控制器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15439894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaFX - Wrapping FXML to Java Class Controller
提问by Leonardo
I have a FXML file that has a TreeView control:
我有一个带有 TreeView 控件的 FXML 文件:
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="500.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test.MyControllerClass">
<TreeView fx:id="locationTreeView" layoutX="12.0" layoutY="158.0" prefHeight="193.0" prefWidth="471.0" />
Then my Java Class Controller needs to wrap with this TreeView and add TreeItem's dynamically. That's the problem, it isn't loading those TreeItem's. That's the test code below, from my Controller:
然后我的 Java 类控制器需要用这个 TreeView 包装并动态添加 TreeItem。这就是问题所在,它没有加载那些 TreeItem。这是下面的测试代码,来自我的控制器:
public class MyControllerClass extends Application {
@FXML
private TreeView<String> locationTreeView;
@Override
public void start(Stage stage) throws Exception {
stage.initStyle(StageStyle.TRANSPARENT);
stage.getIcons().add(new Image(getClass().getResourceAsStream("myIcon.png")));
Parent root = FXMLLoader.load(getClass().getResource("myInterface.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
loadTreeItems();
stage.show();
}
// Just a simple example that still doesn't works
private void loadTreeItems() {
try {
TreeItem<String> root = new TreeItem<String>("Root Node");
root.setExpanded(true);
root.getChildren().addAll(
new TreeItem<String>("Item 1"),
new TreeItem<String>("Item 2"),
new TreeItem<String>("Item 3")
);
locationTreeView = new TreeView<String>(root);
} catch (Exception exc) {
System.out.println("Error: " + exc.getMessage());
}
}
public static void main(String[] args) {
launch(args);
}
}
Any ideas why its not working?
任何想法为什么它不起作用?
回答by jewelsea
There are a couple of reasons why your application doesn't work:
您的应用程序不起作用的原因有两个:
- You need to make the Controller and the Application separate classes.
- You should allow the FXML system to inject the TreeView instance rather than creating a new instance (as Aaron points out in his answer).
- 您需要使控制器和应用程序分开的类。
- 您应该允许 FXML 系统注入 TreeView 实例而不是创建一个新实例(正如 Aaron 在他的回答中指出的那样)。
The way you have your application currently structured what will happen is:
您当前构建应用程序的方式是:
- The Java system will create an instance of
MyControllerClass
on startup (and invoke it'sstart
method). - The
FXMLLoader
will create another instanceofMyControllerClass
each timethemyInterface.fxml
file is loaded. - The
FXMLLoader
will create a newTreeView
instance and perform the FXML injection on thelocationTreeView
member of the newMyControllerClass
instance it creates. - The
FXMLLoader
will try to invoke theinitialize
method on the newMyControllerClass
(of which you have none). - The
FXMLLoader
will notinvoke thestart
method on the newMyControllerClass
. - Your original
start
method invocation on your originalMyControllerClass
will continue processing and create another newTreeView
instance which it will set thelocationTreeView
member of the oldMyControllerClass
instance to.
- Java 系统将
MyControllerClass
在启动时创建一个实例(并调用它的start
方法)。 - 在
FXMLLoader
将创造另一个实例的MyControllerClass
每一次的myInterface.fxml
文件被加载。 - 该
FXMLLoader
会创建一个新的TreeView
实例和上执行FXML注射locationTreeView
的成员新MyControllerClass
它创建实例。 - 该
FXMLLoader
会尝试调用initialize
的新方法MyControllerClass
(其中,你有没有)。 - 该
FXMLLoader
会不调用start
的方法新MyControllerClass
。 - 您对原始
start
方法的原始方法调用MyControllerClass
将继续处理并创建另一个新TreeView
实例,它将旧实例的locationTreeView
成员设置为。MyControllerClass
I updated your code to make the modifications I suggested above and the code now works. The updated code is available.
我更新了您的代码以进行我上面建议的修改,代码现在可以工作了。更新的代码可用。
A sample screenshot from the running code is:
运行代码的示例屏幕截图是:
MyApplicationClass.java
我的应用程序类.java
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.stage.*;
import javafx.util.Duration;
/** Sample application to demonstrate programming an FXML interface. */
public class MyApplicationClass extends Application {
@Override public void start(final Stage stage) throws Exception {
// load the scene fxml UI.
// grabs the UI scenegraph view from the loader.
// grabs the UI controller for the view from the loader.
final FXMLLoader loader = new FXMLLoader(getClass().getResource("myInterface.fxml"));
final Parent root = (Parent) loader.load();
final MyControllerClass controller = loader.<MyControllerClass>getController();
// continuously refresh the TreeItems.
// demonstrates using controller methods to manipulate the controlled UI.
final Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(3),
new TreeLoadingEventHandler(controller)
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
// close the app if the user clicks on anywhere on the window.
// just provides a simple way to kill the demo app.
root.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent t) {
stage.hide();
}
});
// initialize the stage.
stage.setScene(new Scene(root));
stage.initStyle(StageStyle.TRANSPARENT);
stage.getIcons().add(new Image(getClass().getResourceAsStream("myIcon.png")));
stage.show();
}
/** small helper class for handling tree loading events. */
private class TreeLoadingEventHandler implements EventHandler<ActionEvent> {
private MyControllerClass controller;
private int idx = 0;
TreeLoadingEventHandler(MyControllerClass controller) {
this.controller = controller;
}
@Override public void handle(ActionEvent t) {
controller.loadTreeItems("Loaded " + idx, "Loaded " + (idx + 1), "Loaded " + (idx + 2));
idx += 3;
}
}
// main method is only for legacy support - java 8 won't call it for a javafx application.
public static void main(String[] args) { launch(args); }
}
MyControllerClass.java
我的控制器类.java
import javafx.fxml.FXML;
import javafx.scene.control.*;
/** Sample controller class. */
public class MyControllerClass {
// the FXML annotation tells the loader to inject this variable before invoking initialize.
@FXML private TreeView<String> locationTreeView;
// the initialize method is automatically invoked by the FXMLLoader - it's magic
public void initialize() {
loadTreeItems("initial 1", "initial 2", "initial 3");
}
// loads some strings into the tree in the application UI.
public void loadTreeItems(String... rootItems) {
TreeItem<String> root = new TreeItem<String>("Root Node");
root.setExpanded(true);
for (String itemString: rootItems) {
root.getChildren().add(new TreeItem<String>(itemString));
}
locationTreeView.setRoot(root);
}
}
myInterface.fxml
我的接口文件
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns:fx="http://javafx.com/fxml" fx:controller="test.MyControllerClass">
<TreeView fx:id="locationTreeView" layoutX="0" layoutY="0" prefHeight="193.0" prefWidth="471.0" />
</AnchorPane>
回答by Aaron
In your loadTreeItems() function you are creating a new TreeView instance. This is replacing the one that is defined in your FXML file and attached to your scene with a new instance that is not part of the scene graph.
在您的 loadTreeItems() 函数中,您正在创建一个新的 TreeView 实例。这是用不属于场景图的新实例替换在 FXML 文件中定义并附加到场景的实例。
To add items to a TreeView that was created via the FXMLLoader you can use the setRoot()function.
要将项目添加到通过 FXMLLoader 创建的 TreeView,您可以使用setRoot()函数。
private void loadTreeItems() {
try {
TreeItem<String> root = new TreeItem<String>("Root Node");
root.setExpanded(true);
root.getChildren().addAll(
new TreeItem<String>("Item 1"),
new TreeItem<String>("Item 2"),
new TreeItem<String>("Item 3")
);
locationTreeView.setRoot(root);
} catch (Exception exc) {
System.out.println("Error: " + exc.getMessage());
}
}