Instance of FXML controller in JavaFX returns null - javafx

I want to create a reference for the FXML Controller by this line in Class Main
Controller controller = (Controller) loader.getController();
to have access to a Controller methode from the Class GrblListener.
Main.controller.updateCurrentPosition(input);
But I always get the error
Exception in thread "EventThread COM5" java.lang.NullPointerException
What's wrong?
Class Main:
public class Main extends Application {
public static Stage stage;
public static Controller controller;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Scene.fxml"));
Parent root = (Parent) loader.load();
Controller controller = (Controller) loader.getController();
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("css/default.css").toExternalForm());
stage.setTitle("...");
stage.setScene(scene);
stage.show();
this.stage = stage;
}
public static void main(String[] args) {
launch(args);
}
}
Class GrblListener:
class GrblListener implements SerialPortEventListener {
#Override
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR() && event.getEventValue() > 0){
try {
String input = GrblSender.serialPort.readString();
System.out.println(input.trim());
Main.controller.updateCurrentPosition(input);
}
catch (SerialPortException ex) {
System.out.println(ex);
}
}
}
}

You are declaring a local variable in start(), and initializing it:
Controller controller = (Controller) loader.getController();
instead of initializing the static variable you declared:
public static Controller controller ;
public void start(Stage stage) {
controller = (Controller) loader.getController();
// ...
}

Related

JavaFX controller doesn't read propertie from application.yml [duplicate]

I'm still fighting with my issue. I want to use Spring Framework in order to incject dependencies and I have to use Spring boot to integrate both.
Unfortunately, in first view autowiring is run correctly, but if I go next Stage, I got still only Null Pointer Exception.
Thats main class:
#SpringBootApplication(scanBasePackages = "boxingchallenge")
public class BoxingChallengeApplication extends Application {
public ConfigurableApplicationContext springContext;
private Parent root;
public static Stage stage;
#Override
public void init() throws Exception {
springContext = SpringApplication.run(BoxingChallengeApplication.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/FXML/start.fxml"));
fxmlLoader.setControllerFactory(springContext::getBean);
root = fxmlLoader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
primaryStage.setTitle("Boxing challenge");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
springContext.stop();
}
public static void main(String[] args) {
launch(BoxingChallengeApplication.class, args);
}
}
Here in first controller class autowiring run cool:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/FXML/creator.fxml"));
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
Here in second stage, autowiring not working:
#Component
public class Creator {
#FXML
public Button ready;
public TextField nation;
public TextField name;
public Boxer boxer;
/*#Autowired
private ApplicationContext context;*/
#Autowired
public void setBoxer(Boxer boxer) {
this.boxer = boxer;
}
public void createdAndPlay(ActionEvent event) {
if (boxer == null)
System.out.println("boxer is null");
else
System.out.println("Injected correctly");
}
}
Thanks, i hope it's going to finished...
#Jewelsea's comment is correct: you must set the controller factory when you load creator.fxml. If you don't do this, the FXMLLoader will create the controller simply by calling its no-arg constructor, so Spring will know nothing about it and will have no opportunity to inject any dependencies.
To do this, all you need is access to the ApplicationContext in Start, and you can inject "well-known objects", of which the ApplicationContext is an example, into your Spring-managed beans:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
#Autowired
private ApplicationContext context ;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/FXML/creator.fxml"));
load.setControllerFactory(context::getBean);
Parent root = loader.load();
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
As an aside, you almost certainly want a new instance of any controller when you load an FXML file, so you should probably make any controllers prototype scope.

JavaFX - How to switch to another tab with mouse click event [duplicate]

So I'm trying to load and save Images into an imageView where the location of the image is chosen through a file browser. I've been working on this for several days now and I'm gonna have a stroke if I can't get it fixed. I've tried everything I can think of. Thank you in advance for helping.
UPDATED:
Here is my main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
public Main(){}
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout(){
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
RootLayout controller = loader.getController();
controller.setMain(this);
primaryStage.show();
}catch(Exception e ){e.printStackTrace();}
}
public void showScreen(){
try{FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane)loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
}catch (Exception e){e.printStackTrace();}
}
public Stage getPrimaryStage(){return primaryStage;}
public static void main(String[] args) {
launch(args);
}
}
Here is the rootLayout:
public class RootLayout {
private Main main;
private Controller controller = new Controller();
public void setMain(Main main){this.main = main;}
#FXML
private void handleOpen(){
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(
"PNG files (*.png)","*png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if(file!= null){
controller.updateImage(file.toURI().toString());
}
}
}
And here is the controller:
public class Controller implements Initializable {
#FXML
ImageView imageView = new ImageView();
String imageURL;
Main main = new Main();
public void setMain(Main main){
this.main = main;
}
public void updateImage(String url){
if(url.length()>=1){
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
}
else{
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
Two things:
Never assign a field whose value is to be injected by an FXMLLoader (e.g. #FXML fields). Doing so is a waste of resources at best and introduces subtle bugs at worst. For instance, if you were to leave the imageView field uninitialized you'd be getting a NullPointerException which would indicate a problem with your setup. Since you do initialize the field, however, you don't get any errors and there's a false impression of the code working.
In your RootLayout controller class, you have:
private Controller controller = new Controller();
That instance of Controller you just created is not linked to any FXML file. And since you initialize the imageView field (see first point) you end up updating an ImageView which is not being displayed anywhere; this is where not initializing said field would have given a nice indication of there being a problem. The solution is to pass the Controller instance created by the FXMLLoader to the RootLayout instance created by the other FXMLLoader.
Also, in the same class you have:
Main main = new Main();
Which is also unnecessary since the created instance of Main is both not the correct instance and is replaced by the call to #setMain(Main) almost immediately.
Assuming your FXML files (which you did not provide) are correct, the Java classes should look more like:
Main.java
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private RootLayout rootLayoutController;
public Main() {}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// store RootLayout instance in field so #showScreen()
// can reference it
rootLayoutController = loader.getController();
rootLayoutController.setMain(this);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void showScreen() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane) loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
// set Controller instance on RootLayout instance
rootLayoutController.setController(controller);
} catch (Exception e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
RootLayout.java
public class RootLayout {
private Main main;
private Controller controller;
public void setMain(Main main) {
this.main = main;
}
public void setController(Controller controller) {
this.controller = controller;
}
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Note extensions should be prefixed with "*."
FileChooser.ExtensionFilter extensionFilter =
new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if (file != null) {
controller.updateImage(file.toURI().toString());
}
}
}
Controller.java
public class Controller implements Initializable {
#FXML ImageView imageView; // leave uninitialized, will be injected
String imageURL;
Main main;
public void setMain(Main main) {
this.main = main;
}
public void updateImage(String url) {
if (url.length() >= 1) {
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
} else {
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
Note: Did not test new code.

how to get access to the field of already launched application [duplicate]

This question already has an answer here:
JavaFX Single Instance Application
(1 answer)
Closed 3 years ago.
inside controller there is a TextArea
the application to check if it is already running just exits without letting another instance start
public class Main extends Application {
private static Controller controller;
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
AnchorPane anchorPane = fxmlLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setScene(new Scene(anchorPane));
primaryStage.setTitle("Hello World");
primaryStage.show();
}
public static void main(final String[] args) throws IOException {
if (Application Launch)) {
//how to access the open application
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
controller = fxmlLoader.getController();
controller.doSomeThing("myText");
System.exit(0);
}else {
launch(args);
}
that is a controller
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class Controller {
#FXML
private TextArea textArea;
public void doSomeThing(String myText) {
textArea.setText(myText);
}
}
So a small example for you:
private static Controller controller;
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent anchorPane = fxmlLoader.load();
controller = fxmlLoader.getController();
controller.doSomeThing("myText");
...
And in the Controller define a public function.
MyController :
public void doSomeThing(String myText) {
myTextField.setText(myText);
}

Spring injection with new Window (FXML)

I use AnnotationConfigApplicationContext to load my spring configuration.
public class Main extends Application {
private AnnotationConfigApplicationContext applicationContext;
#Override
public void init() throws Exception {
applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
}
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/test/view/main.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();
primaryStage.show();
primaryStage.setOnHidden(e -> Platform.exit());
}
and this is my ApplicationConfig
#Configuration
#ComponentScan
public class ApplicationConfig {
#Bean
public Executor executor() {
return Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
}
Now I would like to inject this Executor instance into a new Stage (TestController.fxml) via FXMLLoader. How can I achieve that?
public void showNewWindow() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/test/view/TestController.fxml"));
Parent root1 = fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
All help will be greatly appreciated. Thanks!
You can inject "well-known objects" into spring-managed beans. The application context itself is one such object, so in your main controller you can do:
public class MainController {
#Autowired
private ApplicationContext applicationContext ;
public void showNewWindow() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/test/view/TestController.fxml"));
fxmlLoader.setControllerFactory(applicationContext::getBean);
Parent root1 = fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
Now the TestController will be spring-managed, so you can simply do
public class TestController {
#Autowired
private Executor executor ;
// ...
}

JavaFx handler override

I have a fxml file build from fxml builder and I am using it by a loader in Java.
URL resource = getClass().getClassLoader().getResource("fxmlFile.fxml");
FXMLLoader loader = new FXMLLoader(resource, resourceBundle);
Pane rootPane = (Pane) loader.load();
this fxml file maps click event to my class;
<Group id="Group" layoutX="0.0" layoutY="0.0" onMouseReleased="#handleThis" scaleX="1.0" scaleY="1.0">
...
<Group/>
so I implement my handler in my class, lets call it MyClass;
public class MyClass {
public void createScene() throws IOException
{
URL resource = getClass().getClassLoader().getResource("fxmlFile.fxml");
FXMLLoader loader = new FXMLLoader(resource, resourceBundle);
Pane rootPane = (Pane) loader.load();
...
}
#FXML
public void handleThis(ActionEvent event) {
System.out.println("from MyClass");
}
...
}
Now I extend MyClass as MyExtendedClass and override handleThis method;
public class MyExtendedClass extends MyClass {
#Override
public void handleThis(ActionEvent event) {
System.out.println("from MyExtendedClass");
}
}
My question is, I cannot manage to work handle method in my extended class. It does not overrides it. How can I achieve to make it print "from MyExtendedClass" instead of "from MyClass"?
When createScene() is called on an instance of MyExtendedClass, the FXMLLoader parses the FXML file, reads the fx:controller="MyClass" attribute and instantiates a new object of type MyClass. That is why the base method is always called. The FXMLLoader doesn't know about MyExtendedClass.
There is a - hackish - way to achieve what you want (i.e. doing the loading in MyClass and still defining the controller in FXML):
public class MyClass
{
public void createScene()
{
try
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
// set a controller factory that returns this instance as controller
// (works in this case, but not recommended)
loader.setControllerFactory(controllerType -> this);
pane = (Pane) loader.load();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
It would be cleaner to instantiate the controller and pass it to the FXMLLoader.
For this the fx:controller="" attribute must be removed from the FXML file.
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
MyClass controller = new MyExtendedClass();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
loader.setController(controller);
Pane pane = (Pane) loader.load();
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Or use fx:controller="MyClass" to define the base type in the FXML file and let a controller factory decide the actual implementation.
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
loader.setControllerFactory(controllerType -> {
if (MyClass.class.equals(controllerType))
return new MyExtendedClass();
else
return null; // return some other controller
});
Pane pane = (Pane) loader.load();
MyClass controller = (MyClass) loader.getController();
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

Resources