I have three ellipses that I need to reference in my FXML Controller:
<Ellipse fx:id="selectorontop" id="selectorontop" fill="WHITE" layoutX="121.0" layoutY="101.0" radiusX="14.0" radiusY="27.0" stroke="WHITE" strokeType="INSIDE" style="-fx-opacity: 70%;" visible="false" />
<Ellipse fx:id="selectoronmiddle" id="selectoronmiddle" fill="WHITE" layoutX="121.0" layoutY="168.0" radiusX="14.0" radiusY="27.0" stroke="WHITE" strokeType="INSIDE" style="-fx-opacity: 70%;" visible="false" />
<Ellipse fx:id="selectoronbottom" id="selectoronbottom" fill="WHITE" layoutX="120.0" layoutY="466.0" radiusX="14.0" radiusY="27.0" stroke="WHITE" strokeType="INSIDE" style="-fx-opacity: 70%;" visible="false" />
The scene is passed to the controller after it is created:
public class QuickCopy extends Application {
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
AnchorPane root = (AnchorPane)loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.sizeToScene();
stage.setResizable(false);
stage.show();
MainController controller = (MainController)loader.getController();
controller.sendScene(scene);
System.out.println("new: " +scene.lookup("selectorontop"));
}
The scene is received by the controller, yet the result of the lookup is still "null", both in the main Java file (seen above), and in the Controller, and I can't figure out why.
Thanks in advance
The selector you're using selects by type, not by id. It selects nodes of type selectorontop and I'm pretty sure this type doesn't exist. (At least there are no nodes of this type in the scene.)
You need to use the proper CSS selector. In this case you need to use #selectorontop to select by id:
System.out.println("new: " +scene.lookup("#selectorontop"));
Related
I'm trying to use the resource tag inside fx:include in a FXML file.
What I don't understand is, that loading the resource bundle manually with ResourceBundle.getBundle() works completely fine.
I already tried a lot of variants in the fxml like:
"lang_challenges"
"wand555/github/io/challengesreworkedgui/lang_challenges"
package wand555.github.io.challengesreworkedgui;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
public class ChallengeApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
ResourceBundle.clearCache();
ResourceBundle bundle = new ResourceBundleWrapper(ResourceBundle.getBundle("wand555/github/io/challengesreworkedgui/lang_challenges"));
System.out.println(bundle.getString("challenge.name")); // outputs "abc"
FXMLLoader loader = new FXMLLoader(ChallengeApplication.class.getResource("overview.fxml"), bundle);
Parent root = loader.load();
Scene scene = new Scene(root, 1000, 1000);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
// this class effectively does nothing, but it will be loaded by the
// application class loader
// instead of the system class loader.
private static class ResourceBundleWrapper extends ResourceBundle {
private final ResourceBundle bundle;
ResourceBundleWrapper(ResourceBundle bundle) {
this.bundle = bundle;
}
#Override
protected Object handleGetObject(String key) {
return bundle.getObject(key);
}
#Override
public Enumeration<String> getKeys() {
return bundle.getKeys();
}
#Override
public boolean containsKey(String key) {
return bundle.containsKey(key);
}
#Override
public Locale getLocale() {
return bundle.getLocale();
}
#Override
public Set<String> keySet() {
return bundle.keySet();
}
}
}
overview.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<AnchorPane prefHeight="500.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="wand555.github.io.challengesreworkedgui.controllers.OverviewController">
<children>
<VBox>
<children>
<StackPane alignment="CENTER_LEFT">
<children>
<Button fx:id="exportButton" mnemonicParsing="false" onAction="#onExport" text="Export" />
</children>
</StackPane>
<HBox prefHeight="100.0" prefWidth="200.0" spacing="25.0">
<children>
<fx:include fx:id="challengesOverview" source="challenges/challenges_overview.fxml" resources="lang_challenges" charset="utf-8"/>
<Separator orientation="VERTICAL" prefHeight="200.0" />
<fx:include source="goals/goal_overview.fxml" />
<Separator orientation="VERTICAL" prefHeight="200.0" />
</children>
</HBox>
</children>
</VBox>
</children>
</AnchorPane>
The lang_challenges.properties contains a single key-value pair
challenge.name=abc
The `lang_challenges_de.properties' has the same content
challenge.name=abc
And this is the error message I'm getting
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at javafx.graphics#19/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
at javafx.graphics#19/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1082)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics#19/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901)
at javafx.graphics#19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: javafx.fxml.LoadException:
/Users/felixnaumann/Documents/ChallengesReworked/ChallengesReworkedGUI/target/classes/wand555/github/io/challengesreworkedgui/overview.fxml:21
at javafx.fxml#19/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2707)
at javafx.fxml#19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2685)
at javafx.fxml#19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
at javafx.fxml#19/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2516)
at challenges.reworked.gui/wand555.github.io.challengesreworkedgui.ChallengeApplication.start(ChallengeApplication.java:22)
at javafx.graphics#19/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847)
at javafx.graphics#19/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
at javafx.graphics#19/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics#19/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics#19/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Caused by: java.util.MissingResourceException: Can't find bundle for base name lang_challenges, locale de_DE
at java.base/java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:2045)
at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1683)
at java.base/java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1575)
at java.base/java.util.ResourceBundle.getBundle(ResourceBundle.java:1280)
at javafx.fxml#19/javafx.fxml.FXMLLoader$IncludeElement.processAttribute(FXMLLoader.java:1100)
at javafx.fxml#19/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
at javafx.fxml#19/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:755)
at javafx.fxml#19/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2808)
at javafx.fxml#19/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2634)
... 9 more
Exception running application wand555.github.io.challengesreworkedgui.ChallengeApplication
Whew, finally figured it out after digging deep into the source code of ResourceBundle and ClassLoader.
How to fix it:
Really easy way:
Put your .properties files at the root of the resources folder. Then use it inside fxml
<fx:include source="second.fxml" resources="second_bundle"/>
and then everything should work fine.
However this approach is not suitable for larger projects which subdivide the resource bundles into different packages.
Using sub-packages in resources package
Give the fully qualified path name in the resources tag.
So for example if your package structure from src is com/example/demo/ (also in the resources folder) then use
<fx:include source="second.fxml" resources="com/example/demo/second_bundle"/>
But we are not done yet. You need to open the package to all modules in the module-info.java, explicitly using
opens com.example.demo to javafx.fxml;
does not work.
Instead you need to write
opens com.example.demo;
My two cents
Honestly to me this sounds like a bug.
I debugged the entire loading process and when the second_bundle is loaded from inside the fxml file, the caller module is javafx.fxml. And as the java doc for getResourceAsStream (which is internally called when loading) states:
A package name is derived from the resource name. If the package name is a package in the module then the resource can only be located by the caller of this method when the package is open to at least the caller's module. If the resource is not in a package in the module then the resource is not encapsulated.
So in theory opening your package to just javafx.fxml should work, but it doesn't...
Full demo project
This demo project uses sub-packages.
Project structure:
HelloApplication:
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
ResourceBundle bundle = new ResourceBundleWrapper(ResourceBundle.getBundle("test_bundle"));
System.out.println(bundle.getString("first.button")); // outputs "English/Deutsch"
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("first.fxml"), bundle);
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
// this class effectively does nothing, but it will be loaded by the
// application class loader
// instead of the system class loader.
private static class ResourceBundleWrapper extends ResourceBundle {
private final ResourceBundle bundle;
ResourceBundleWrapper(ResourceBundle bundle) {
this.bundle = bundle;
}
#Override
protected Object handleGetObject(String key) {
return bundle.getObject(key);
}
#Override
public Enumeration<String> getKeys() {
return bundle.getKeys();
}
#Override
public boolean containsKey(String key) {
return bundle.containsKey(key);
}
#Override
public Locale getLocale() {
return bundle.getLocale();
}
#Override
public Set<String> keySet() {
return bundle.keySet();
}
}
}
first.fxml:
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.demo.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="%first.button" onAction="#onHelloButtonClick"/>
<fx:include source="second.fxml" resources="com/example/demo/second_bundle"/>
</VBox>
second.fxml:
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="%second.button"/>
</VBox>
test_bundle.properties:
first.button=English
second_bundle.properties:
second.button=Hello
module-info.java:
module com.example.demo {
requires javafx.controls;
requires javafx.fxml;
opens com.example.demo;
exports com.example.demo;
}
I came across a problem in my code that all the objects that were related to the FXML file for a controller class were null even though the styling from the FXML was working and all the fx:id tags were the same. Here is the FXML code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
alignment="center"
spacing="10"
prefHeight="750"
prefWidth="1000"
style="-fx-background-color: lightslategray">
<padding><Insets top="0" bottom="10" left="10" right="10"></Insets></padding> <!-- Set the padding at 10px for each side of the window -->
<Label fx:id="titleLabel" style="-fx-font-weight: bold; -fx-font-size: 32;" wrapText="true" text="Deck Title"/>
<HBox spacing="10">
<Button fx:id="backButton" text="Back" prefWidth="50"/>
<ProgressBar fx:id="progressIndicator" GridPane.columnIndex="0" GridPane.rowIndex="0" prefWidth="920" progress="0.0"/>
</HBox>
<HBox spacing="20">
<Label fx:id="qLabel" style="-fx-background-color: white; -fx-border-color: black" prefWidth="480" prefHeight="400" wrapText="true"/>
<Label fx:id="aLabel" style="-fx-background-color: white; -fx-border-color: black; -fx-cursor: hand" prefWidth="480" prefHeight="400" wrapText="true" text="Click here to reveal the answer" onMouseClicked="#updateAnswer"/>
</HBox>
<HBox spacing="780">
<Button fx:id="incorrectButton" text="Incorrect" prefWidth="100"/>
<Button fx:id="correctButton" text="Correct" prefWidth="100"/>
</HBox>
</VBox>
Here is the code for the controller class:
public class openCardsController {
#FXML Button backButton;
#FXML ProgressBar progressIndicator;
#FXML Label qLabel;
#FXML Label aLabel;
#FXML Label titleLabel;
#FXML Button incorrectButton;
#FXML Button correctButton;
public void openCards() throws IOException, ParseException {
Stage window = Main.getStage();
window.setWidth(1000);
window.setHeight(750);
// Had to swap Parent root = FXMLLoader.load(getClass().getResource("./mainPage.fxml")); for the following lines
File file = new File(System.getProperty("user.dir") + "/src/flashcardApplication/openCardsPage.fxml");
FXMLLoader loader = new FXMLLoader(file.toURI().toURL());
loader.setController(this);
VBox root = loader.load();
backButton.setOnAction(e -> {
try {
backButtonPressed();
} catch (IOException ignored) {}
});
incorrectButton.setOnAction(e -> incorrect());
correctButton.setOnAction(e -> correct());
window.setTitle("Flashcard Application - Open Cards");
Scene mainMenuScene = new Scene(root, 1000, 750);
window.setScene(mainMenuScene);
int deckid = chooseCards();
String fileURL = "ftp://appuser:pass123.#127.0.0.1/decks/" + Integer.toString(deckid) + ".json";
URL url = new URL(fileURL); // Lines 43 to 45 come from https://www.javaworld.com/article/2073325/java-ftp-client-libraries-reviewed.html
URLConnection urlc = url.openConnection();
InputStream inputStream = urlc.getInputStream();
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject)jsonParser.parse(new InputStreamReader(inputStream, "UTF-8"));
String title = (String) jsonObject.get("name");
titleLabel.setText(title);
}
I had to remove the attribute fx:controller="flashcardApplication.openCardsController from the FXML file and I had to replace the line Parent root = FXMLLoader.load(getClass().getResource("./mainPage.fxml")); with the following lines:
File file = new File(System.getProperty("user.dir") + "/src/flashcardApplication/openCardsPage.fxml");
FXMLLoader loader = new FXMLLoader(file.toURI().toURL());
loader.setController(this);
Please could someone explain why I had to use the different solution because I used the one line solution in two other FXML files and their corresponding controller classes without any issues
The #FXML-annotated fields are initialized in the controller when the FXML is loaded. By default, the FXMLLoader creates an instance of the class specified by the fx:controller attribute, and uses it as the controller.
Consequently, in your original code, the controller is not the current instance of OpenCardsController on which openCards() is being invoked, but is the new instance of the same class. Thus the #FXML-annotated fields are not initialized in the current instance, but in the new instance that is created by the FXMLLoader.
By removing the fx:controller attribute, and explicitly setting the controller to the exact object you need (the current instance of OpenCardsController), you achieve what you need: the controller is now the current instance and the FXML-annotated fields are initialized in that object.
Note that it's a bit unusual to load the FXML file from the controller itself. The typical approach is to load the FXML from some other code, and display the resulting UI; the controller is usually a separate object. It may be more natural (and easier to maintain in the long run) if you refactor your code so that the FXML is loaded from somewhere else. (This is really a separation of concerns and single-responsibility issue: the controller should only be responsible for processing user input from the corresponding FXML file; it should not be responsible for loading the FXML as well.)
I'm trying update a form when a table row is clicked. Usually the error happen in the first click, but updates the form properly.
java.lang.ClassCastException: class com.sun.javafx.geom.BoxBounds cannot be cast to class com.sun.javafx.geom.RectBounds (com.sun.javafx.geom.BoxBounds and com.sun.javafx.geom.RectBounds are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader #5419f379)
at com.sun.javafx.sg.prism.NGNode.applyTransform(NGNode.java:2484)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1226)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.sg.prism.NGNode.accumulateGroupDirtyRegion(NGNode.java:1208)
at com.sun.javafx.sg.prism.NGNode.accumulateDirtyRegions(NGNode.java:1041)
at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:248)
at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
at java.base/java.lang.Thread.run(Thread.java:834)
I was using Java 11 (OpenJDK) and JavaFx 2, then I found some answers that it could be some incompatibilty, then I moved to JavaFx 13, but the problem is still happening. If I stop the code in the event method onMouseClicked() the error doesn't happen, however if pass the mouse over the table is enough to throw it.
equipmentTableView.setOnMouseClicked((MouseEvent event) -> {
if (event.getButton().equals(MouseButton.PRIMARY)) {
int index = equipmentTableView.getSelectionModel().getSelectedIndex();
equipment = equipmentTableView.getItems().get(index);
loadData(equipment);
}
});
<TableView layoutX="20.0" layoutY="200.0" prefHeight="200.0" prefWidth="450.0" fx:id="equipmentTableView">
<columns>
<TableColumn prefWidth="100.0" text="Nome" fx:id="nameColumn">
<cellValueFactory><PropertyValueFactory property="name"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="I.P." fx:id="ipColumn">
<cellValueFactory><PropertyValueFactory property="ipAddress"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="160.0" text="Serial" fx:id="dicomEquipmentIdColumn">
<cellValueFactory><PropertyValueFactory property="dicomEquipmentId"/></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Sala" fx:id="roomNickNameColumn">
<cellValueFactory><PropertyValueFactory property="roomId"/></cellValueFactory>
</TableColumn>
</columns>
<effect>
<InnerShadow/>
</effect>
</TableView>
I'm using Springboot 2.2 with FXMLloader defined in the class:
public class SpringFXMLLoader {
private final ResourceBundle resourceBundle;
private final ApplicationContext context;
public SpringFXMLLoader(ResourceBundle resourceBundle, ApplicationContext context) {
this.resourceBundle = resourceBundle;
this.context = context;
}
public Parent load(String fxmlPath) throws IOException {
FXMLLoader loader = getFxmlLoader(fxmlPath);
return loader.load();
}
public Pane loadPane(String fxmlPath) throws IOException {
FXMLLoader loader = getFxmlLoader(fxmlPath);
return loader.load();
}
private FXMLLoader getFxmlLoader(String fxmlPath) {
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(context::getBean);
loader.setResources(resourceBundle);
loader.setLocation(getClass().getResource(fxmlPath));
return loader;
}
}
Any help is welcome! Thanks
I'm not sure what was the problem, however it was solved by moving the table declaration from FXML file to the controller in initialize().
I use osgi+cdi and I have the following code:
Parent parent=null;
FXMLLoader fxmlLoader=getFxmlLoader();
try {
parent = (Parent)fxmlLoader.load(getFxmlStream("tasklist.fxml"));
} catch (IOException ex) {
Logger.getLogger(TestGoView.class.getName()).log(Level.SEVERE, null, ex);
}
ComboBox comboBox=(ComboBox) parent.lookup("#testComboBox");
if (comboBox==null){
System.out.println("COMBOBOX NULL");
}else{
System.out.println("COMBOBOX NOT NULL");
}
And I have the following tasklist.fxml
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="440.0" prefWidth="757.0" xmlns="http://javafx.com/javafx/8.0.60-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.techsenger.testgo.core.adm.task.list.TaskDirListController">
<children>
<HBox>
<children>
<ToolBar maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" nodeOrientation="RIGHT_TO_LEFT" HBox.hgrow="SOMETIMES">
<items>
<ComboBox fx:id="testComboBox" maxWidth="1.7976931348623157E308" nodeOrientation="LEFT_TO_RIGHT" />
</items>
</ToolBar>
</children>
</HBox>
</children>
</VBox>
However parent.lookup("#testComboBox") returns null. How to explain it? I've checked the name of ID several times.
Instead of using a lookup, which will only work after the scene has been rendered, you can put the logic you need in your controller class. You can inject elements from the FXML file into the controller class by annotating them #FXML.
public class TaskDirListController {
#FXML
private ComboBox<...> testComboBox ;
public void initialize() {
System.out.println(testComboBox);
}
// ...
}
Lookups are generally not robust, and I would recommend avoiding using them. If you really need to access something defined in the FXML file from a class other than the controller, the first thing to do is to consider reorganizing things so that you don't need to do this: it really indicates that your overall design is wrong.
If you really need this for some reason, it's better to use the FXMLLoader's namespace than a lookup:
Parent parent=null;
FXMLLoader fxmlLoader=getFxmlLoader();
try {
parent = (Parent)fxmlLoader.load(getFxmlStream("tasklist.fxml"));
ComboBox<?> comboBox = (ComboBox<?>) fxmlLoader.getNamespace().get("testComboBox");
System.out.println(comboBox);
} catch (IOException ex) {
Logger.getLogger(TestGoView.class.getName()).log(Level.SEVERE, null, ex);
}
It is because you are trying to use lookup before showing the parent on the screen.
Pane root = FXMLLoader.load(getClass().getResource("tasklist.fxml"));
System.out.println(root.lookup("#testComboBox")); //returns null
primaryStage.setScene(new Scene(root));
primaryStage.show();
System.out.println(root.lookup("#testComboBox")); //returns
// ComboBox[id=testComboBox, styleClass=combo-box-base combo-box]
I've been trying to move my JavaFx application to Spring to make use of dependency injecton. The project has become too big to move items between classes manually.
The code does not show any red error highlights on my netbeans IDE, but when I run I keep getting this error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'homeController' defined in class wakiliproject.controllerinjection.SampleAppFactory: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public wakiliproject.controllerinjection.controllers.HomeController wakiliproject.controllerinjection.SampleAppFactory.homeController() throws java.io.IOException] threw exception; nested exception is javafx.fxml.LoadException: Base location is undefined.
I have it like this:
public class SampleApp extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) throws Exception {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(SampleAppFactory.class);
Group box = new Group();
HomeController homeController = context.getBean(HomeController.class);
box.getChildren().add(homeController.getView());
Scene scene = new Scene(box, 600, 200);
stage.setScene(scene);
stage.setTitle("JFX2.0 Sprung");
stage.show();
}
}
--
#Configuration
public class SampleAppFactory {
#Bean
public HomeController homeController() throws IOException {
return (HomeController) loadController("/resources/fxml/Home.fxml");
}
protected Object loadController(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.load(fxmlStream);
return loader.getController();
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
}
--
public class HomeController {
#FXML
private AnchorPane view;
public AnchorPane getView() {
return view;
}
}
An extract of the FXML file looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<AnchorPane fx:id="view" prefHeight="654.0" prefWidth="900.0" styleClass="AnchorPane" visible="true" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="wakiliproject.controllerinjection.controllers.HomeController">
<children>
<Pane id="wrapperPane" prefHeight="600.0" prefWidth="700.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Pane id="top" layoutX="-1.0" layoutY="3.0" prefWidth="879.0" styleClass="appNav" visible="true">
<children>
<HBox id="HBox" alignment="CENTER" layoutX="12.0" layoutY="6.0" spacing="0.0">
<children>
<Label prefHeight="32.0" prefWidth="32.0" styleClass="titleBarHome" />
<Label prefHeight="32.0" prefWidth="32.0" styleClass="titleBarPublisher" />
</children>
</HBox>
It looks like you need to set the location of your FXML file on the FXMLLoader. This can be the case if, for example, your FXML file requires resolving relative locations. Using the FXMLLoader.load(URL) method does not set the location. Try
protected Object loadController(String url) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(url));
loader.load();
return loader.getController();
}
instead of the current implementation you have.
Incidentally, have you looked at afterburner.fx as a (much more lightweight) alternative to Spring for dependency injection in JavaFX?