Explain why FXML objects were null - java

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.)

Related

NullPoinerException in javafx when looking up objects

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"));

NullPointer Exception thrown with JavaFX

My application is throwing a NullPointer exception when I call the variable empName.
ResultSet result = stmnt.executeQuery("select FirstName, LastName from emp_info where EmployeeID "
+ "= '" + empID + "'");
// while(result.next()){
// empName.setText("result.getString(1));
// }
empName.setText("asdf");
rootLayout.setCenter(controlData);
connection.close();
}
}
It should be able to run fine since I initialize the Text variable like so:
#FXML
public Text empName;
Also when I use scenebuilder, it sometimes shows the fx:id empName but when I exit out of it and reopen it doesn't show it. I think that's where the problem is. My xml file is:
<AnchorPane prefHeight="300.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="studentempsignin.MainPage_Controller">
<children>
<BorderPane prefHeight="300.0" prefWidth="500.0">
<center>
<Text fx:id="empName" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
</children>
</AnchorPane>
The exception I get is:
SEVERE: null
java.lang.NullPointerException
at studentempsignin.MainPage_Controller.signingIn(MainPage_Controller.java:190)
at studentempsignin.SignIn_Controller.lambda$0(SignIn_Controller.java:66)
at studentempsignin.SignIn_Controller$$Lambda$143/575703892.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
Edit:
The method empIDAccess is being called when I click the signIn button:
signInBtn.setOnAction((e) -> {
try {
//used dbaseDriver...idk why
// DBaseDriver connect = new DBaseDriver("jdbc:mysql://localhost:3306/AUStudentEmployees",
// "kheneahm", "kennygoham");
String empID = empIDField.getText();
MainPage_Controller empIDAccess = new MainPage_Controller();
empIDAccess.signingIn(empID, invalidID, signInBtn);
} catch (Exception ex) {
Logger.getLogger(SignIn_Controller.class.getName()).log(Level.SEVERE, null, ex);
}
});
You're creating a new controller instance. The FXMLLoader creates a controller instance and initializes the #FXML-annotated fields in that instance, when you load the FXML file. Those fields won't be initialized in the new instance you create (and even if they were, they wouldn't refer to the same objects displayed in the UI).
You need to get a reference to the controller that is created when you load the FXML.

JavaFX MongoDB representing collection in a TreeView [duplicate]

This question already has answers here:
Is #FXML needed for every declaration?
(3 answers)
Closed 7 years ago.
I try to represent mongodb collection in javafx treeview and here is my code.
This treeView has to have a name of an ingredient as a parent and fields(variables) as children and their children suppose to be values of those variables.
#FXML
protected TreeView treeView;
public void editIngredient() {
TreeItem root, name, measurement, calories;
root = new TreeItem();
MongoDatabase db = MongoConnection.getMongoDatabase();
MongoCollection<Document> ingredients = db.getCollection("ingredients");
MongoCursor<Document> cursor = ingredients.find().iterator();
while (cursor.hasNext()) {
// retrieved a doc
Document ingredient = cursor.next();
String ingredientName = ingredient.getString("name");
name = makeBranch(ingredientName, root);
// measurement
measurement = makeBranch("measurement", name);
makeBranch(ingredient.getString("measurement"), measurement);
//calories
calories = makeBranch("calories", name);
makeBranch(ingredient.getDouble("calories"), calories);
}
treeView = new TreeView(root);
treeView.setShowRoot(false);
}
// creating branches
private TreeItem makeBranch(Object title, TreeItem parent){
TreeItem item = new TreeItem(title);
parent.getChildren().add(item);
return item;
}
here is fxml file:
<center>
<Pane fx:id="mainPane" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: #3c3f41;" BorderPane.alignment="CENTER" >
<children>
<TreeView fx:id="treeView" layoutX="14.0" layoutY="14.0" prefHeight="348.0" prefWidth="200.0"/>
<TextArea layoutX="239.0" layoutY="14.0" prefHeight="205.0" prefWidth="342.0"/>
</children>
</Pane>
</center>
But the treeView doesn't show anything..
What's wrong with is? Thank you in advance!
p.s. pls tell if any additional info needed
I haven't read the code in detail, but
parent.getChildren().add(parent);
tries to make a tree item (parent) a child of itself. Any attempt to traverse the tree is then going to result in infinite recursion.
Presumably you mean
parent.getChildren().add(item);

JavaFx: parent.lookup returns null

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]

Error creating a custom FXML loader in Spring

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?

Categories

Resources