Null pointer exception in #FXML Injection - java

I'm trying to work with JavaFX and FXML files, but something goes wrong when I inject the MenuBar in the controller, compiler gives me a NullPointerException. I tried with buttons and textfields and it works.
This is mycode:
FXML file:`
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox fx:controller="it.fedgiac.projectmanager.controller.ControllerMain" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar fx:id="menubar_mainmenu" />
</children>
</VBox>`
And this is my Controller
public class ControllerMain {
#FXML
public MenuBar menubar_mainmenu;
public void generateView(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("main_layout.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Projects Manager");
menubar_mainmenu.getMenus().add(new Menu("File"));
stage.setScene(scene);
stage.show();
}
}
After debugging I saw that the variable menubar_mainmenu is null
This is the error:
Thank you in advance for your help.

Make your FXML Controller implement Initializable. You will then be prompted to implement the method initialize(URL url, ResourceBundle resourceBundle). In this method, you can be sure that the menubar_mainmenuis initialized. You can move your existing code into that method.
public void initialize(URL url, ResourceBundle resourceBundle){
menubar_mainmenu.getMenus().add(new Menu("File"));
}

Related

Why does this JavaFX application look way different than when it's in SceneBuilder?

I was trying to make my first application on JavaFX but this frustrated me so much! These elements of the application are perfect in SceneBuilder, but now it's just misaligned!
I think it's because of this error when I run the app:
WARNING: Loading FXML document with JavaFX API of version 11.0.1 by JavaFX runtime of version 8.0.231
I have tried to change the AnchorPane attributes to:
<AnchorPane prefHeight="129.0" prefWidth="205.0" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"">
...
</AnchorPane>
but it just fixes the warning and not the misalignment.
Here is my full code:
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.stage.Stage;
import javafx.scene.Scene;
//import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
//BorderPane root = new BorderPane();
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="129.0" prefWidth="205.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TextField layoutX="28.0" layoutY="23.0" />
<Button layoutX="77.0" layoutY="65.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
As #Slaw mentioned that AnchorPane is not a responsive layout. You may consider using VBox with CENTER alignment as you have two nodes positioned with vertically.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Pos?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<VBox prefHeight="129.0" alignment="CENTER" prefWidth="205.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TextField fx:id="INPUT_FIELD" />
<Button fx:id="SUBMIT" mnemonicParsing="false" text="Button" />
</children>
</VBox>
Generally, AnchorPane can be set as a parent layout and you can put VBox on it and controls(Button, TextField, etc) can be added on the VBox.
I think it's because of this error when I run the app:
WARNING: Loading FXML document with JavaFX API of version 11.0.1 by JavaFX runtime of version 8.0.231
No, it's just a warning! You can find a huge discussion over here.

field that is supposed to be assigned by fxml is throwing NullPointerException when accesed from inner class - javaFX

I have a controller for an fxml file. The controller has a field public BorderPane mainBorderPane; which is supposed to be filled with the Border pane of the same fx:id found in the fxml file. When I try to access it from an inner class it gives a NullPointerExcetion
The clearBorderPaneCenter(); and the clearBorderPaneRight work just fine but when I run the cashSceneController.show() it crashes on the line mainBorderPane.setRight(rightLayout);
fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="mainBorderPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Program.gui.MainSceneController">
<top>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</top>
<left>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</left>
<center>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
<right>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</right>
<bottom>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>
Simplified version of controller:
public class MainSceneController {
MainSceneController.CashSceneController cashSceneController = new MainSceneController.CashSceneController();
public BorderPane mainBorderPane;
public void switchLayout(byte ID){
//todo Make this work switchScene()
clearBorderPaneCenter();
clearBorderPaneRight();
cashSceneController.show();
}
public void clearBorderPaneRight(){
try {
mainBorderPane.setRight(null);
OutputHelper.log("cleared right of mainBorderPane");
} catch (Exception e){
OutputHelper.log("clearing right of mainBorderPane not required - already cleared");
}
}
public void clearBorderPaneCenter(){
try {
mainBorderPane.setCenter(null);
OutputHelper.log("cleared centre of mainBorderPane");
} catch (Exception e){
OutputHelper.log("clearing centre of mainBorderPane not required - already cleared");
}
}
public class CashSceneController{
VBox rightLayout;
public void show() {
setVBox();
mainBorderPane.setRight(rightLayout);
}
public void setVBox(){
rightLayout = new VBox();
//......
}
}
}
This is how the fxml file is loaded
SceneController = new MainSceneController(new Scene(FXMLLoader.load(getClass().getResource("gui/MainScreen.fxml"))));
I hope that I explained my problem well enough. I'm new to stack overflow and have no idea of what is a good question
Edit: it appears that the problem is caused by the mainBorderPane not being assigned at all. The clearBorderPaneCenter and clearBorderPaneRight seem to not crash cause they are caught by the try catch. Any ideas why the mainBorderPane might not be getting assigned correctly?
You are missing the fxml annotation. It is required for the FXMLLoader to inject the BorderPane into the code
#FXML
public BorderPane mainBorderPane;

Access directly to object declared into fxml file

I'm pretty new to Java FX and fxml templates, I know that to access to an object's methods you need to use setOnAction and declare a specific method into the controller class.
Is there a way to access to its methods from code without using setOnAction?
Example what I want to do:
FXML Document
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="package.Controller">
<children>
<Label fx:id="myLabel" text="Label" />
</children>
</VBox>
Controller class
public class Controller {
#FXML
Label myLabel;
public Controller() {
this.mylabel = new Label();
//some code here
myLabel.setText("modified text 0");
//some code here
myLabel.setText("modified text 1");
}
}
Is that possible in some way by not using setOnAction custom method?

SubScene in JavaFX does not show elements loaded from an FXML file

I have a program created with an FXML file (made in SceneBuilder) that has four SubScenes in it:
#FXML
public SubScene p1sub;
#FXML
public SubScene p2sub;
#FXML
public SubScene p3sub;
#FXML
public SubScene p4sub;
Each of these subscenes is nearly identical to the rest.
I can get the root node (which contains these) to show up just fine, but when I try to add the SubScenes, they don't show up.
//This is the code I use to initialize one of the four.
Parent root2a = null;
try {
FXMLLoader loader2 = new FXMLLoader(getClass().getResource(
"PlayerConfigurationSubScreen.fxml"));
root2a = (Parent) loader2.load();
} catch (Exception e) {
/*code*/
}
/*code*/
if (root2a != null) {
System.out.println("root2 isn't null");
p1sub = new SubScene(root2a, 149, 260);
}
/*code*/
stage.show();
Any idea how to make them show up? I'm new at JavaFX.
Set the subscene dimensions in fxml and instead of "new" just add the parent to the subscene.
....
p1sub.setRoot(root2a);
....
I think you are approaching the problem the wrong way. From what I understand from your code, you want to load a subscene and then assign it to one of your predefined "slots".
This however will not work, as the pre-defined slots has nothing to do with what is actually rendered. Everything that is drawn in a window has to be assigned as child of a container of some kind.
I've made an example here that reproduces the problem you are facing:
public class ReplaceTest extends Application {
#FXML Button button;
#FXML Label oldLabel;
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load( getClass().getResource("ReplaceTest.fxml") );
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private static int i = 0;
#FXML protected void simpleClick(ActionEvent e){
Label newLabel = new Label("Hello! " + i++);
oldLabel = newLabel;
System.out.println("Nothing happens...");
}
}
And the FXML file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="replaceTest.ReplaceTest">
<children>
<Label fx:id="oldLabel" text="TEXT TO BE REPLACED" />
<Button fx:id="button" onAction="#simpleClick" text="Replace label" />
</children>
<padding>
<Insets top="10.0" />
</padding>
</VBox>
If you try this example, you will see that overwriting oldLabel does not affect the rendering window. Instead, you can try to create areas to which you add your new subscenes as children. This will update the rendering window and will (hopefully) produce the behavior you desire. Again, I've made an example you can consult:
public class ReplaceTest extends Application {
#FXML Button button;
#FXML VBox wrappingBox;
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load( getClass().getResource("ReplaceTest.fxml") );
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private static int i = 0;
#FXML protected void simpleClick(ActionEvent e){
Label newLabel = new Label("Hello! " + i++);
wrappingBox.getChildren().clear();
wrappingBox.getChildren().add(newLabel);
System.out.println("Someting happens!");
}
}
And the FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="replaceTest.ReplaceTest">
<children>
<VBox fx:id="wrappingBox" alignment="TOP_CENTER">
<children>
<Label text="TEXT TO BE REPLACED" />
</children>
</VBox>
<Button fx:id="button" onAction="#simpleClick" text="Replace label" />
</children>
<padding>
<Insets top="10.0" />
</padding>
</VBox>

Accessing node generated by FXML

I've created a simple JavaFX program, using CSS and FXML for style and layout, respectively. How can I, from the main Java application, edit the nodes declared in the FXML? Specifically, how could I change the text from the Text object shown below:
CSS
.stage {
-fx-background-color: lightskyblue ;
-fx-effect: innershadow(three-pass-box , rgba(0,0,0,0.6), 25, 0.0, 0, 1 );
}
.time {
-fx-fill: white;
-fx-font-size: 95;
-fx-font-family: 'sans-serif';
-fx-effect: dropshadow(three-pass-box , rgba(0,0,0,0.6), 2, 0.0, 0, 1 );
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<BorderPane fx:id="mainStage" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" styleClass="stage" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
<center>
<Text fx:id="time" styleClass="time" text="Text" />
</center>
<stylesheets>
<URL value="#application.css" />
</stylesheets>
</BorderPane>
Java Program
public class Main extends Application {
#Override
public void start(Stage stage) {
Parent root;
try {
root = FXMLLoader.load(getClass().getResource("layout.fxml"));
Scene scene = new Scene(root, 300, 275);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Add a controller class for the FXML, with the proper annotations:
public class MyCtrl {
#FXML private Text time; // name must be the same as fx:id
}
Declare the controller in the FXML (there are other ways too, this is probably the simplest):
<BorderPane fx:id="mainStage" ... fx:controller="mypackage.MyCtrl">
And then you can access it from within the controller, e.g. from the initialize() method:
public void initialize() {
text.setText("xyz");
}
(You probably want to setup a timer in MyCtrl.initialize() to call setText() with the current time periodically.)

Categories

Resources