1

Let's say I have three files:

  • Controller.java
  • Creator.java
  • Scene.fxml

Scene.fxml's controller is set to Controller.java. Controller.java calls a method from Creator.java which creates a new scene using FXMLLoader.load( ) method, then passes this scene to newly created stage and returns this stage. Controller.java calls .show() method on that returned stage. Everything is great so far, the window did pop up, but the issue is that I can not access any node from Scene.fxml in Controller.java. I naturally have "fx:id" stuff in Scene.fxml and I've also created all those nodes in Controller.java with the exact name and @FXML annotation. Anyway, they are all set to NULL.

I assume that the issue might be connected with FXMLLoader.load() method being called from s class that is not the class controller. So the question is: am I right? i If I am, is there any way to actually make it work the way I want?

If the explanation is not good enough I will create a minimal reproducible example.

EDIT:

Controller.java:

package Issue;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class Controller extends Application {

    @FXML
    private Label label;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage = Creator.createStage(stage);
        stage.show();
        System.out.println("Is label null?");
        if(label == null){
            System.out.println("yes");
        } else {
            System.out.println("no");
        }
    }
}

Creator.java:

package Issue;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Creator {

    public static Stage createStage(Stage stage)  {
        Parent root = null;
        String pathToProject = System.getProperty("user.dir");
        Path path = Paths.get(pathToProject, "src", "main", "java", "Issue", "Scene.fxml");
        try{
            URL url = new URL("file", "", path.toString());
            root = FXMLLoader.load(url);
        } catch (IOException | NullPointerException e) {
            System.out.println("wrong url to fxml");
        }


        stage.setScene(new Scene(root, 400, 400));
        return stage;
    }
}

Scene.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="Issue.Controller"
            prefHeight="400.0" prefWidth="600.0">
    <center>
        <Label fx:id="label" text="hey stackoverflow"/>
    </center>
</BorderPane>

module-info.java:

module Main {
    requires javafx.controls;
    requires javafx.fxml;

    opens Issue to javafx.fxml;
    exports Issue;
}

I've created standard Maven projects and created "Issue" package with those files in src/main/java path. module-info.java is in src/main/java.

MrJ_
  • 55
  • 7
  • "I assume that the issue might be connected with FXMLLoader.load() method called from class that is not the class controller. So the question is: am I right?" -> No. "is there any way to actually make it work the way I want?" -> probably. Edit the question, provide a [mcve], *minimal*, *complete* code which replicates the issue (copy and paste to compile and run). (Please read the suggestions in the link twice). If you do so, likely somebody will point out your error. – jewelsea Sep 20 '21 at 19:54
  • It is highly unlikely that your code "creates a new scene using FXMLLoader.load( ) method". Probably, you have the load() method creating a subclass of `Parent` and create a `Scene` in your code using a `new` call. – jewelsea Sep 20 '21 at 19:58
  • "I naturally have fx:id stuff in Scene.fxml and I've also created all those nodes in Controller.java with the exact name and @FXML annotation." -> You should *never* create nodes (e.g. make a `new` call to initialize a value) for nodes marked `@FXML`, the loader will automatically create these nodes for you. Perhaps your wording was just wrong or ambiguous, anyway if you supply the code, it would be possible to verify without going through comments and responses. – jewelsea Sep 20 '21 at 20:12
  • 1
    @jewelsea I've provided mre. – MrJ_ Sep 20 '21 at 20:19
  • 2
    The way you create the path to lookup FXML is wrong. It will work when the FXML is in a file and break when the application is packaged into a jar file. See: [How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?](https://stackoverflow.com/questions/61531317/how-do-i-determine-the-correct-path-for-fxml-files-css-files-images-and-other) – jewelsea Sep 20 '21 at 20:23
  • 1
    Your `createStage` doesn't actually create a stage at all. Instead, it initializes (loads a scene) into a stage that was already created, so it should be called `initStage` (or some other more meaningful name, such as `loadScene`) not `createStage`. – jewelsea Sep 20 '21 at 20:32
  • 1
    `static` methods are, in general, usually not a good thing to have in your application and should be replaced with an instance method. You can either create an instance in your code or (for a more advanced method), use dependency injection, e.g. spring to create the instance. In your case, it would be best to replace the static method and create and manage the `Creator` instance in your code rather than adding a dependency on a complex dependency injection framework. – jewelsea Sep 20 '21 at 20:36
  • 1
    I (or somebody else) could create in an answer, a working example of your code, but I suggest you try to apply some of the above comments yourself and see if you can resolve your issue yourself. Note some of the suggestions are just suggestions they aren't actual errors (those others do point out real errors). – jewelsea Sep 20 '21 at 20:37
  • 3
    An fxml controller should *never* extend `Application`. Applications have a lifecycle that is managed by the JavaFX runtime. They should only be created by the application `launch()` method. There should only be one Application instance ever created for the java virtual machine. An FXML loader will, by default, create a new instance of a controller. If your controller is also an application, then you will have two instances of Application in the virtual machine, which is highly confusing and error-prone. An application that is a controller violates the separation of concerns principle. – jewelsea Sep 20 '21 at 20:48
  • Thank you so much for all this. There is a lot of it, but learning some good practices will probably save me more time in the future than doing it the dirty way now. One question is: how can I actually create a valid URL path to file? In the link you've posted, the author suggested using getClass().getResource(...) method. What if for example I have FXML file in Maven's resources directory? There isn't any class in it or above it and in such case I can't really use this way. – MrJ_ Sep 20 '21 at 20:50
  • 2
    I think the linked answer on resource lookup covers this, but I agree that answer is quite complex and a bit difficult to follow because it is so comprehensive. For the specific question of looking up fxml from a resource directory, see: [How to reference javafx fxml files in resource folder?](https://stackoverflow.com/questions/19602727/how-to-reference-javafx-fxml-files-in-resource-folder). It is really important that you manually check the actual build output directory or jar contents to see if the resource is where expected. – jewelsea Sep 20 '21 at 20:55
  • 1
    Also note, that if you don't want to use `getClass()` to determine the current class and load from a relative location to that. You can use an absolute class name (either the current class or another) and load from a location relative to that class (e.g. `Controller.class.getResource(...)`). – jewelsea Sep 20 '21 at 20:59
  • Thanks so much, the second link was a lot more clearer to me and I've managed to fix my paths to FXMLs and CSSs. I will try to improve my app with all the info I've got from you. It will not be easy since I have to edit a lot of code, but I wouldn't like to get into more troubles when I go further with my code. Thanks again! – MrJ_ Sep 20 '21 at 21:22
  • Also update your package name to match [java naming conventions](https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html) (and your module, which follows the [similar naming conventions](https://stackoverflow.com/questions/43192741/how-should-i-name-my-java-9-module)). – jewelsea Sep 20 '21 at 22:48

0 Answers0