JavaFX fx:id doesn´t create a Object in the controller class - javafx

Hello I am new to JavaFX and have problems with the fx:id.
I get a null pointer exception in my controller class.
The objects apple and snake are not created, although the fx:id and declarations in the controller class match.
Can someone help me?
Main-Class:
public class Main extends Application {
private Controller controll = new Controller();
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("World2.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
scene.setOnKeyPressed(controll);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Controller-Class:
public class Controller implements EventHandler<Event> {
#FXML
private ImageView snake;
#FXML
private ImageView apple;
private int snakeRow = 8;
private int snakeColumn = 10;
private int appleRow = 5;
private int appleColumn = 5;
#Override
public void handle(Event ev) {
KeyEvent event = (KeyEvent) ev;
System.out.println("handle-Methode");
if (event.getCode() == KeyCode.LEFT && snakeColumn > 0) {
snakeColumn--;
System.out.println("Snake-ID: " + snake);
System.out.println("Apple-ID: " + apple);
}
}
FXML:
<ImageView fx:id="apple" fitHeight="39.0" fitWidth="44.0" pickOnBounds="true" GridPane.columnIndex="5" GridPane.rowIndex="5">
<image>
<Image url="#../img/icons8-apfel-48.png" />
</image>
</ImageView>
<ImageView fx:id="snake" fitHeight="36.0" fitWidth="44.0" pickOnBounds="true" GridPane.columnIndex="10" GridPane.rowIndex="8">
<image>
<Image url="#../img/icons8-hydra-48.png" />
</image>
</ImageView>

A controller instance is not associated with a scene unless
It's created by FXMLLoader based on the fx:controller attribute in the fxml. In this case you'd need to retrieve the controller instance from the FXMLLoader instance used to load the fxml after loading:
FXMLLoader loader = new FXMLLoader(getClass().getResource("World2.fxml"));
Parent root = loader.load();
Controller controll = loader.getController();
or
You don't specify the fx:controller attribute but pass a Object to FXMLLoader before loading that is to be used as controller for the scene:
FXMLLoader loader = new FXMLLoader(getClass().getResource("World2.fxml"));
loader.setController(controll);
Parent root = loader.load();

Related

NullPointerException passing a string from a window to another

Long story short, I searched for similar problems to mine, so I could troubleshoot this "by my own". I found these examples here and here, but none of them worked for me.
I need to pass to the next window a string that was typed in the TextField of the first window.
My main:
public class Main extends Application{
/**
* #param stage
* #throws java.lang.Exception
*/
#Override
public void start(Stage stage) throws Exception {
FXMLLoader floader = new FXMLLoader();
Parent root = FXMLLoader.load(getClass().getResource("java1.fxml"));
Java1Controller j1 = (Java1Controller)floader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
As I'm using FXML, here's the controller of Window1 (java1):
public class Java1Controller implements Initializable {
#FXML
private TextField tSend;
#FXML
private Button bSend;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void sendText(ActionEvent event) throws IOException {
FXMLLoader fLd = new FXMLLoader();
Parent novo = FXMLLoader.load(getClass().getResource("java2.fxml"));
Java2Controller j2 = (Java2Controller)fLd.getController();
j2.setLbText(tSend.getText());
Scene novScene = new Scene(novo);
Stage novStage = (Stage)((Node)(event.getSource())).getScene().getWindow();
novStage.setScene(novScene);
novStage.setResizable(false);
novStage.show();
}
}
And the controller of Window2 (java2):
public class Java2Controller implements Initializable {
#FXML
private Label lbText;
#FXML
private Button bOK;
/**
* Initializes the controller class.
* #param url
* #param rb
*/
/**
* #param string the string to set
*/
public void setLbText(String string) {
lbText.setText(string);
}
#FXML
private void closeJPane(ActionEvent event) {
Stage stage = (Stage)((Node)(event.getSource())).getScene().getWindow();
stage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
The problem is pointed to the Java1Controller line where I setText with my custom function (j2.setLbText). Why? If it matters, I'm using Netbeans 8.2 and JavaFX Scene Builder 11.0.
FXML Java1:
<Pane maxHeight="252.0" maxWidth="388.0" minHeight="252.0" minWidth="388.0" prefHeight="252.0" prefWidth="388.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.Java1Controller">
<children>
<Label layoutX="126.0" layoutY="76.0" text="Insira texto aqui:">
<font>
<Font size="16.0" />
</font>
</Label>
<TextField fx:id="tSend" layoutX="109.0" layoutY="102.0" />
<Button fx:id="bSend" layoutX="169.0" layoutY="178.0" mnemonicParsing="false" onAction="#sendText" text="Send" />
</children>
</Pane>
FXML Java2:
<Pane maxHeight="388.0" maxWidth="252.0" minHeight="252.0" minWidth="388.0" prefHeight="252.0" prefWidth="388.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.Java2Controller">
<children>
<Label fx:id="lbText" layoutX="137.0" layoutY="106.0" text="Texto enviado">
<font>
<Font size="16.0" />
</font>
</Label>
<Button fx:id="bOK" layoutX="164.0" layoutY="178.0" mnemonicParsing="false" onAction="#closeJPane" prefHeight="26.0" prefWidth="61.0" text="OK" />
</children>
</Pane>
You're using static load methods to load the fxmls. This way a new FXMLLoader instance is created by the method that you cannot get a reference to. The FXMLLoader instance you create yourself is never used to load an fxml file, so no controller is available via its getController property:
// here you create a loader without setting a location
FXMLLoader fLd = new FXMLLoader();
// here a static method uses another loader instance to load the fxml
Parent novo = FXMLLoader.load(getClass().getResource("java2.fxml"));
// here you try to retrieve the controller from the loader instance that is still in the same state as after the constructor completion
Java2Controller j2 = (Java2Controller)fLd.getController();
Change your code an use the loader instance you created yourself to load the fxml:
// create loader with location set
FXMLLoader fLd = new FXMLLoader(getClass().getResource("java2.fxml"));
// use loader instance to load fxml from location specified
Parent novo = fLd.load();
// the controller should now be available
Java2Controller j2 = (Java2Controller)fLd.getController();
assert j2 != null

How to add a javafx shortcut key combinations for buttons

My UI has a adding button and I want to assign a keyboard shortcut combination for that. I have failed to use the setAcceleartor for this purpose.
What is the easiest way to set up keyboard shortcuts in javafx applications?
button declaration in the UI:
<Button fx:id="addButton" alignment="CENTER" minWidth="-Infinity" mnemonicParsing="false" onAction="#addAction" prefHeight="31.0" prefWidth="130.0" text="Add" HBox.hgrow="ALWAYS" />
Controller button binding:
#FXML
private Button addButton;
The method that wants to setOnAction for the shortcut for the button:
public void addAction(ActionEvent event) throws SQLException, ClassNotFoundException {
if (validateInput()) {
String productName = productField.getText();
double unitPrice = Double.parseDouble(priceField.getText());
int quantity = Integer.parseInt(quantityField.getText());
double total = unitPrice * quantity;
ITEMLIST.add(new Item(productName, unitPrice, quantity, total));
calculation();
resetAdd();
productTableView.getSelectionModel().clearSelection();
ObservableList<Product> productsData = ProductDAO.searchProducts();
populateProducts(productsData);
searchField.setText("");
}
}
initialize() method:
#FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {
setSaveAccelerator(addButton);
}
The code I tried with setAccelerator:
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
In your setSaveAccelerator method, instead of directly calling addAction(ActionEvent event), just instruct the button to fire its event to its listeners such as: button.fire(). For example:
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button is null! "); // check that the button was injected properly through your fxml
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
EDIT
To also avoid the IllegalArgumentException you must attach the accelerator after the button is attached to a scene. I managed to achieve this by creating a public method in the controller to attach the accelerator after the scene has been set. Then, in the class where the scene is loaded the controller's method can be called which sets up this functionality. See the example below:
In the controller class (in my case MainController):
public void setup() {
setSaveAccelerator(button);
}
In your main class when loading the fxml file:
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
controller.setup(); // calls the setup method attaching the accelerators
FULL EXAMPLE
Main class:
public class Main extends Application{
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
primaryStage.setTitle("Shortcut example");
primaryStage.setScene(scene);
controller.setup();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maincontroller:
public class MainController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private Button button;
#FXML
private AnchorPane rootPane;
#FXML
private TextArea textarea;
#FXML
void action(ActionEvent event) {
textarea.setText("Action fired!!");
}
#FXML
void initialize() {
assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert textarea != null : "fx:id=\"textarea\" was not injected: check your FXML file 'MainFXML.fxml'.";
}
public void setup() {
setSaveAccelerator(button);
}
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button null!!");
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
}
MainFXML.fxml
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="button" layoutX="139.0" layoutY="350.0" mnemonicParsing="false" onAction="#action" text="Button" />
<TextArea fx:id="textarea" layoutX="73.0" layoutY="38.0" prefHeight="200.0" prefWidth="200.0" />
</children>
</AnchorPane>

How to add node to another node's child in FXML?

Example of what I'd like to get, written in Java code:
public class Main extends Application {
private static Scene scene;
public static void main(String[] args) {
launch(args);
}
#Override
public void init() throws IOException {
// Load root pane from FXML file.
URL url = getClass().getResource("sample.fxml");
StackPane root = FXMLLoader.load(url);
// Create scene for a root node on JavaFX thread.
Platform.runLater(() -> scene = new Scene(root, 600, 400));
}
#Override
public void start(Stage stage) {
stage.setScene(scene);
stage.show();
}
}
Custom node:
public class CustomGroup extends Group {
private VBox contentPane = new VBox();
public CustomGroup() {
getChildren().add(contentPane);
contentPane.getChildren().add(new Label("First Label"));
contentPane.getChildren().add(new Label("Second Label"));
}
}
FXML:
<StackPane>
<CustomGroup/>
</StackPane>
The code above is example of what I'd like to get, but instead of adding labels in Java code, I want to add them in FXML. Something like that:
<StackPane>
<CustomGroup>
<Label text="First Label"/>
<Label text="Second Label"/>
</CustomGroup>
</StackPane>
but this adds labels to the custom group. I want to add them to the content pane (VBox) of the custom group.
Though, I am not sure why you are adding the Labels to your VBox inside the constructor of CustomGroup, I will ignore it and answer your question.
You can add a separate method to add the items to your VBox. Let us consider the methods:
setItems() which accepts Nodes adds them to the VBox
getItems() which returns the ObservableList<Node> from the VBox
CustomGroup
public class CustomGroup extends Group {
private VBox contentPane = new VBox();
public CustomGroup() {
getChildren().add(contentPane);
contentPane.getChildren().add(new Label("First Label"));
contentPane.getChildren().add(new Label("Second Label"));
}
public void setItems(Node...nodes) {
contentPane.getChildren().addAll(nodes);
}
public ObservableList<Node> getItems() {
return contentPane.getChildren();
}
}
FXML
<CustomGroup>
<items>
<Button text="hi"/>
</items>
</CustomGroup>
This FXML adds the new Button inside the VBox.

On Mouse Entered Method to swap Button position

I would like to develop a mouse entered method that swaps the locations of two buttons in real time using FXML and JavaFX which I am unfortunately very new to. Relocate(x,y), get/setLayoutX/Y and below get/setTranslateX/Y all throw IllegalArgumentEceptions with not much more understandable information in the stack trace. What is the preferred Button Property to use in order to get and then set a real-time location swap?
#FXML protected void neinHover (ActionEvent evt){
double jTmpX, jTmpY, nTmpX, nTmpY;
nTmpX = neinButton.getTranslateX();
nTmpY = neinButton.getTranslateY();
jTmpX = jaButton.getTranslateX();
jTmpY = jaButton.getTranslateY();
jaButton.setTranslateX(nTmpX);
jaButton.setTranslateY(nTmpY);
neinButton.setTranslateX(jTmpX);
neinButton.setTranslateY(jTmpY);
}
I supose you want something like this:
FXML:
<fx:root onMouseClicked="#swap" type="Pane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<children>
<Button fx:id="b1" mnemonicParsing="false" text="1" />
<Button fx:id="b2" layoutX="90.0" mnemonicParsing="false" text="2" />
</children>
</fx:root>
Controller:
public class MockPane extends Pane {
#FXML
private Button b1;
#FXML
private Button b2;
public MockPane() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"MockPane.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#FXML
private void swap() {
double b1x = b1.getLayoutX();
double b1y = b1.getLayoutY();
double b2x = b2.getLayoutX();
double b2y = b2.getLayoutY();
b1.setLayoutX(b2x);
b1.setLayoutY(b2y);
b2.setLayoutX(b1x);
b2.setLayoutY(b1y);
}
}
App:
public class MockApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
MockPane root = new MockPane();
Scene scene = new Scene(root, 200, 100);
stage.setScene(scene);
stage.show();
}
}

External jar FXML Load Exception

I try to load a class located in an external jar using classloader. The class "FXMLbase" loads ok, but the error is triggered when the FXMLdocument.fxml try to instantiate the FXMLDocumentController. However when the "FXMLbase" is instantiate throught the JavaFXApplication5.java (located at the external jar) it works fine. Any Ideas?
Class Loader
File file = new File("C:/Users/Os/Dropbox/CODE_OS/JavaFXApplication5/dist/JavaFXApplication5.jar");
URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()}, this.getClass().getClassLoader());
Class c = clazzLoader.loadClass("javafxapplication5.FXMLbase");
PluginInterface fXMLbase = (PluginInterface) c.newInstance();
Parent loadScreen = fXMLbase.getRoot();
FXMLbase.java -- external jar --
public Parent getRoot() {
Parent root = null;
try {
System.out.println("Class Name:" + getClass().getName());
root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
} catch (IOException ex) {
Logger.getLogger(FXMLbase.class.getName()).log(Level.SEVERE, null, ex);
}
return root;
}
FXMLdocument.fxml -- external jar --
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="javafxapplication5.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="126" layoutY="90" onAction="#handleButtonAction" text="Click Me! app5" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
</children>
FXMLDocumentController.java -- external jar --
public class FXMLDocumentController implements Initializable{
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
JavaFxApplication5.java -- external jar --
public void start(Stage stage) throws Exception {
FXMLbase fXMLbase=new FXMLbase();
Parent root = fXMLbase.getRoot();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Error:
ago 28, 2014 2:26:16 PM javafxapplication5.FXMLbase getRoot
SEVERE: null
javafx.fxml.LoadException:
file:/C:/Users/Os/Dropbox/CODE_OS/JavaFXApplication5/dist/JavaFXApplication5.jar!/javafxapplication5/FXMLDocument.fxml:9
....
Caused by: java.lang.ClassNotFoundException: javafxapplication5.FXMLDocumentController
The FXMLLoader at some point has to load the controller class from the value of the fx:controller attribute in the root element of the FXML file. It looks like it is using the system class loader to do this: I think this is because the system class loader finds the FXMLLoader class and loads it, rather than the class loader used to load your FXMLBase class.
The only fix I can find for this is to explicitly set the controller class from the FXMLbase class, instead of specifying it in the FXML. This is a bit unsatisfactory; perhaps there is a better way I am missing.
Updated FXMLbase class:
public Parent getRoot() {
Parent root = null;
try {
System.out.println("Class Name:" + getClass().getName());
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
loader.setController(new FXMLDocumentController());
root = loader.load();
} catch (IOException ex) {
Logger.getLogger(FXMLbase.class.getName()).log(Level.SEVERE, null, ex);
}
return root;
}
and you need to remove the fx:controller attribute from the FXML file.

Resources