This question already has an answer here:
JavaFX, Label null pointer exception
(1 answer)
Closed 1 year ago.
I'm working on a Game project with JavaFX and a MVC Architecture
Here are one of my views
package fr.arnoux23u.javano.mvc.views;
import fr.arnoux23u.javano.mvc.*;
import javafx.fxml.*;
import javafx.scene.control.TextArea;
import java.net.URL;
import java.util.ResourceBundle;
/**
* #author arnoux23u
*/
public class ServerView implements Initializable, Observer {
#FXML
private TextArea clientsList;
#Override
public synchronized void update(Model m) {
System.out.println("[UPDATE] "+Thread.currentThread().getName());
clientsList.setText("test");
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
System.out.println("[INITIALIZE] "+Thread.currentThread().getName());
clientsList.setText("Aucun joueur ici");
}
}
And my FXML File
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fr.arnoux23u.javano.mvc.views.ServerView">
<Label text="JavaNo - Server">
<font>
<Font size="23.0" />
</font>
</Label>
<Label alignment="CENTER" text="Connected Clients">
<font>
<Font size="23.0" />
</font>
</Label>
<TextArea fx:id="clientsList" editable="false" maxWidth="200.0" prefHeight="200.0" prefWidth="200.0" style="-fx-focus-color: -fx-control-inner-background;" text="
">
<VBox.margin>
<Insets top="20.0" />
</VBox.margin>
<font>
<Font size="18.0" />
</font></TextArea>
</VBox>
When I load the app, the text of the TextArea clientsList is correctly set to "Aucun joueur ici" but when I want to update the text (with MVC), I've a NullPointerException on clientsList
First, I thought it was a Thread Error because the Thread who runs the update method was not the "JavaFX Application Thread"
So in my MVC Controller, I use the Platform.runlater() method.
#Override
public void notifyObservers() {
observers.forEach(o -> {
System.out.println("[FOREACH] "+Thread.currentThread().getName());
Platform.runLater(() -> {
System.out.println("[PLATFORM] "+Thread.currentThread().getName());
o.update(this);
});
});
}
The output when I call the update is
[INITIALIZE] JavaFX Application Thread
[FOREACH] Thread-3
[PLATFORM] JavaFX Application Thread
[UPDATE] JavaFX Application Thread
But even with the runLater method, I've a NullPointerException
Here is the complete stack trace
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.control.TextArea.setText(String)" because "this.clientsList" is null
at fr.arnoux23u.javano/fr.arnoux23u.javano.mvc.views.ServerView.update(ServerView.java:34)
at fr.arnoux23u.javano/fr.arnoux23u.javano.mvc.Game.lambda$notifyObservers$1(Game.java:54)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:833)
I hope someone can help me.
Thank's
See step 3 of this description of FXMLoader operation:
If there is a fx:controller attribute on the root element, the FXMLLoader creates an instance of the specified class.
So, if you create a new instance explicitly in your code, and the loader also creates a new instance, then only the loader instance will have fxml values injected.
I guess, with a high degree of certainty, that is what is happening with your code.
Related
I make a program using FXML. In FXML I create 4 TextFields and Button. My problem is how send parameters from TextFields in FXML to Controller, when I click this button.
I'm make JavaFX application with FXML. I connect this application to database. I searched for the previous week unsuccessfully, but I found nothing special that can help me.
<children>
<BorderPane prefHeight="30.0" prefWidth="700.0">
<bottom>
<AnchorPane>
<children>
<Button text="Add car" fx:id="button" onAction="#AddCar"/>
</children>
</AnchorPane>
</bottom>
</BorderPane>
<TextField promptText="Brand" id="carBrand"/>
<TextField promptText="Model" id="carModel"/>
<TextField promptText="Mileage" id="carMileage"/>
</children>
I expect that I click Button in FXML - Java controller get parameters from FXML and write to the console. Actual I know how to write in console, but my problem is that I don't know how to get brand, model and mileage from FXML.
You need to inject your TextFields into your controller, then query their text properties. To do this, specify an fx:id for each element to be injected and add a field to your controller class with the same type and name. If the field is not public then you need to annotate it with #FXML.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/12.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.Controller" spacing="10" alignment="center">
<TextField fx:id="carBrand" promptText="Brand"/>
<TextField fx:id="carModel" promptText="Model"/>
<TextField fx:id="carMileage" promptText="Mileage"/>
<Button text="Add car" onAction="#addCar"/>
</VBox>
package com.example;
import javafx.fxml.FXML;
import javafx.event.ActionEvent;
import javafx.scene.control.TextField;
public class Controller {
#FXML private TextField carBrand;
#FXML private TextField carModel;
#FXML private TextField carMileage;
#FXML
private void addCar(ActionEvent event) {
event.consume();
String brand = carBrand.getText();
String model = carModel.getText();
String mileage = carMileage.getText();
// do something with values...
}
}
Note: Following Java naming conventions, method names use camelCase. In other words, the button's action method's name should be addCar (like above) rather than AddCar.
I'm using Scene Builder (v11.0.0) to create FXML files for scenes in JavaFX (v12) but, despite instructing all containers to USE_COMPUTED_SIZE for the preferred widths and heights, the rendered scenes (as seen in Scene Builder and also when run as a JavaFX application which loads those FXML files) are being clipped at the right and bottom edges so that bits of nodes are chopped off.
And in Scene Builder it seems that the renderer must know that the scene won't fit the allowed bounds because the editor shows blue boundary markers which are clearly some way beyond the rendered rectangle.
View in Scene Builder
The view in Scene Builder shows that more space is needed at the bottom in order to give the buttons sufficient space (their bottom edge, and the lower edge of the TitledPane is missing). And more space is needed at the right in order to fit the right edges of the DatePicker and TitledPane. The blue boundary markers show clearly where the actual content ends, so it's not clear why the display area is being calculated to be several pixels shorter than this.
View of running Java application
Once the FXML files are used to populate a window in a JavaFX application, the same thing is seen: the calculated size for the window is a number of pixels too few to fit the whole scene correctly.
If the blue boundary markers have correctly been calculated to show that extra display area width and height are needed, how do I tell the FXML to require this additional space when rendering?
Is this a known bug/limitation in Scene Builder, FXML, or JavaFX. Or is there something more I need to do beyond just selecting USE_COMPUTED_SIZE for the preferred dimensions?
In order to make this explicit, see the example FXML below which displays the problem illustrated.
scene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox alignment="BASELINE_RIGHT">
<children>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</content>
</TitledPane>
</children>
</VBox>
subscene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
<DatePicker />
</children>
</VBox>
This does appear to be a bug in JavaFX, specifically DatePicker, as this simple example can reproduce the problem:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(new DatePicker());
// Problem shows up when using USE_COMPUTED_SIZE (the default) as well
root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
root.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Resulting in:
Note: It does not seem to matter what parent the DatePicker is put in. Nor does the problem appear with other controls.
A workaround to this issue appears to be calling Window.sizeToScene() after calling show(). I don't understand why that would make a difference, but it does. Unfortunately, this will only help in the real application, not in Scene Builder.
I am trying to make basic desktop app using Kotlin and JavaFX(TornadoFX), but I am stuck on setting up controller for FXML file.
I am following guide from edvin on git LINK to git.
The program should increment number in label whenewer is the button clicked.
The problem is..
When I try to use FXML file shown below, I can't use onAction="#increment" to "connect" that file with a controller.
This will compile, but there is no way to call increment function from controller.kt file. Also there is error saying "No controller specified for top level element" ..
Whenever I try to specify the controller by using fx:controller=view.Controller the code will not even compile, showing error:
javafx.fxml.LoadException: Controller value already specified.
Could someone please help me out?
Here is FXML file:
?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<center>
<VBox alignment="CENTER" spacing="10">
<Label text="0">
<font>
<Font size="20"/>
</font>
</Label>
<Button text="Click to increment" onAction="#increment"/>
</VBox>
</center>
</BorderPane>
Here is MyApp.kt (main file):
package app
import javafx.stage.Stage
import tornadofx.*
import view.MainView
class MyApp: App(MainView::class){
override val primaryView = MainView::class
override fun start(stage: Stage) {
stage.minHeight = 400.0
stage.minWidth = 600.0
super.start(stage)
}
}
Here is MainView.kt (view):
package view
import javafx.scene.layout.BorderPane
import tornadofx.*
class MainView : View() {
override val root: BorderPane by fxml("/views/MainViewFXML.fxml" )
}
Here is Controller.kt (this should be used to control FXML file actions):
package view
class Controller {
fun increment(){
//code
}
}
In TornadoFX, the View IS the controller. Think of View subclasses as the View Controller. React to UI events in the View, and pass business logic off to a ViewModel or a Controller subclass.
Place your increment function in MainView and it will be called :) Remove the fx:controller=view.Controller attribute.
I am trying to create a simple button, but I am getting this stupid error and it doesn't make any sense.
Here is my Admin Scene FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="437.0" prefWidth="582.0" stylesheets="#application.css" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="appController.AdminSceneController">
<children>
<Separator layoutX="-14.0" layoutY="101.0" prefHeight="10.0" prefWidth="601.0" />
<Label layoutX="14.0" layoutY="29.0" text="admin panel">
<font>
<Font name="Book Antiqua" size="28.0" />
</font>
</Label>
<Button layoutX="174.0" layoutY="32.0" mnemonicParsing="false" style="-fx-background-radius: 100px;" text="+" textFill="#369033" />
<Button fx:id="logoutButton" layoutX="14.0" layoutY="65.0" mnemonicParsing="false" onAction="#logout" prefHeight="3.0" prefWidth="81.0" styleClass="logout" stylesheets="#application.css" text="(logout)" textFill="#070707" />
<Button layoutX="387.0" layoutY="392.0" mnemonicParsing="false" prefHeight="31.0" prefWidth="158.0" text="Delete" />
<ListView layoutX="223.0" layoutY="106.0" prefHeight="327.0" prefWidth="128.0" />
</children>
</Pane>
And this is my AdminSceneController.java
package appController;
import appDesign.PhotoAlbum;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
public class AdminSceneController {
public class MainSceneController {
#FXML
Button logoutButton;
#FXML
public void logout(ActionEvent event) throws Exception {
PhotoAlbum.primaryStage.show();
((Node)(event.getSource())).getScene().getWindow().hide();
}
}
}
I get a warning from Eclipse saying:
The controller 'AdminSceneController' has no event slot 'logout'
And when I run the program, I get the error:
javafx.fxml.LoadException: Error resolving onAction='#logout', either the event handler is not in the Namespace or there is an error in the script.
/C:/Users/Peter/Documents/GitHub/PhotoAlbum40/bin/appDesign/AdminPanelScene.fxml:19
Can anybody help?
Your AdminSceneController does not have a logout method, your class MainSceneController does though.
Remove the line
public class MainSceneController {
and the closing }and it should work.
You have to understand, that an inner class (MainSceneController) is not the same class as the enclosing class (AdminSceneController). By using fx:controller="appController.AdminSceneController" in the fxml a instance of AdminSceneController is created. This class however does not contain a single method or field. This causes the error.
Furthermore note that the FXMLLoader does not allow you to create non-static inner classes. If you want the FXMLLoader to create your controller instance, you have to make MainSceneController static and use fx:controller="appController.AdminSceneController$MainSceneController".
Ways around this would be specifying a controllerFactory or creating the controller instance yourself:
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
AdminSceneController enclosingInstance = new AdminSceneController(); // or any other way to get your hands on a instance of the enclosing class
// specify controller instance used yourself
loader.setController(enclosingInstance.new MainSceneController());
...
loader.load()
Which requires you to remove the fx:controller attribute from the fxml.
Of course you could also simply move the field / method to a top level class...
This question already has answers here:
How to implement a NumberField in javaFX 2.0?
(8 answers)
Closed 9 years ago.
I'm new to Java and JavaFX, but I'm trying to build a pretty basic program. Below is a snippet of code I had originally placed in the On Key Pressed event of the TextField (it did of course have a different parameter type). However, the code didn't work there (i.e. the user was still allowed to enter characters), and after some Googling I found that maybe this code should be moved to the On Input Method Text Changed event of the TextField. But, I'm unable to determine how I can recover the key that was pressed and whether or not CTRL is down.
private void verifyKeyIsInteger(InputMethodEvent event) {
KeyCode code = event.getCode();
if (event.isControlDown() && (code.equals(KeyCode.C) || code.equals(KeyCode.X) || code.equals(KeyCode.V))) {
return;
}
else if (code.isDigitKey()) {
return;
}
event.consume();
}
This code is in my controller that is attached to the FXML file.
How can I ensure that user's can only input integers into the TextField?
EDIT
Here is my current FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="tutoringcalculator.MainFormController">
<children>
<Label layoutX="14.0" layoutY="14.0" text="Session Minutes:" />
<TextField fx:id="sessionMinutes" layoutX="100.0" layoutY="14.0" prefWidth="200.0" />
<Label layoutX="14.0" layoutY="34.0" text="Earnings:" />
<TextField fx:id="earnings" layoutX="100.0" layoutY="34.0" prefWidth="200.0" />
<Button fx:id="quitButton" layoutX="511.0" layoutY="14.0" minWidth="75.0" mnemonicParsing="false" onMouseClicked="#quitApplication" text="Quit" />
</children>
</AnchorPane>
If you need to track integer input, you can have a look at the implementation of IntegerField of JavaFX (a field which is used to access only integer values) :
IntegerField.java
IntegerFieldSkin.java
But this approach is not applicable, if CTRL is needed to be tracked.