I have a FXML file that has a TreeView control:
<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:
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?
There are a couple of reasons why your application doesn't work:
The way you have your application currently structured what will happen is:
MyControllerClass
on startup (and invoke it's start
method).FXMLLoader
will create another instance of MyControllerClass
each time the myInterface.fxml
file is loaded.FXMLLoader
will create a new TreeView
instance and perform the FXML injection on the locationTreeView
member of the new MyControllerClass
instance it creates.FXMLLoader
will try to invoke the initialize
method on the new MyControllerClass
(of which you have none).FXMLLoader
will not invoke the start
method on the new MyControllerClass
.start
method invocation on your original MyControllerClass
will continue processing and create another new TreeView
instance which it will set the locationTreeView
member of the old MyControllerClass
instance to.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
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
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>
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.
To add items to a TreeView that was created via the FXMLLoader you can use the setRoot() function.
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());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With