0

I have: 2 FXML Files, and 2 Controllers. Each FXML's have the controllers merged. This is the code opening the new FXML (While the real fxml is running it opens another one)

try {
            Parent root = FXMLLoader.load(getClass().getResource(
                    "settings.fxml"));
         Stage stage = new Stage();
         stage.setScene(new Scene(root));  
         stage.centerOnScreen();
         stage.show();
        } catch (Exception e) {
            e.printStackTrace();
            Logger.logCrash("ApplicationScene", e);
        }

This is the controller of the FXML file which gets opened

    @FXML
    public TextField destination;
    @FXML
    public TextArea view;
    @FXML
    public TextArea point;


    public void initialize() {
        destination.appendText("LOL");
        view.appendText("LAA");
        point.appendText("LOWKAPF");
    }

As you can see, I'm appending text on all declared fields (FXML-ID'S ARE BOUND!) after the root has been loaded through the initialize method. Sounds good and great, but I get a NullPointerException.

To point the things out clearly: - I've already bound the fxml-ids to their corresponding components. - The FXML file gets loaded up correctly (root is being loaded correctly, else the initialize method wouldnt work)

This has nothing to do with the static access. Even without static access this does not work.

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

<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="373.0" prefWidth="518.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="removed">
   <children>
      <TextField fx:id="destination" layoutX="14.0" layoutY="19.0" prefHeight="25.0" prefWidth="464.0" promptText="Destination of the files" text="ie.: C:\" />
      <Text layoutX="14.0" layoutY="57.0" strokeType="OUTSIDE" strokeWidth="0.0" text="moo" wrappingWidth="464.0" />
      <TextArea fx:id="point" layoutX="14.0" layoutY="76.0" prefHeight="42.0" prefWidth="464.0" promptText="HI/>
      <Text layoutX="14.0" layoutY="131.0" strokeType="OUTSIDE" strokeWidth="0.0" text="meow" wrappingWidth="464.0" />
      <TextArea fx:id="view" layoutX="14.0" layoutY="135.0" prefHeight="42.0" prefWidth="464.0" promptText="HI" />
      <Text layoutX="14.0" layoutY="191.0" strokeType="OUTSIDE" strokeWidth="0.0" text="m00" wrappingWidth="464.0" />
      <Button layoutX="220.0" layoutY="269.0" mnemonicParsing="false" onAction="#initialize" text="Default" />
   </children>
</AnchorPane>

Oh. Ok, so, What I did is was:

    package com.engine.application.content;

import com.engine.Logger;

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

public class Settings extends Application {

    public static void start() {
        Application.launch();
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource(
                "settings.fxml"));
        Scene scene = new Scene(root);
        setStageProperties(primaryStage, scene);

    }

    /**
     * Sets the properties of the application
     * 
     * @param stage
     *            the stage's properties to set
     * @param scene
     *            the scene's properties to set
     */
    private void setStageProperties(Stage stage, Scene scene) {
        stage.setScene(scene);
        stage.setTitle("Test");
        stage.centerOnScreen();
        stage.setResizable(true);
        stage.show();
        Logger.log("Launcher", "Set stage properties");
    }

}

Then I'm calling
Application.start() when a button is clicked

This is the result:

Caused by: java.lang.IllegalStateException: Application launch must not be called more than once
    at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at javafx.application.Application.launch(Unknown Source)
    at xxx.Settings.start(Settings.java:14)
    at xxx.openMenu(ApplicationScene.java:43)
    ... 56 more

I'm not calling it somewhere else btw.

EDIT: This is the Settings application init.

public class Settings extends Application {

public static void main(String[] args) {
    launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource(
            "settings.fxml"));
    Scene scene = new Scene(root);
    setStageProperties(primaryStage, scene);
    System.out.println("YAY");

}

/**
 * Sets the properties of the application
 * 
 * @param stage
 *            the stage's properties to set
 * @param scene
 *            the scene's properties to set
 */
private void setStageProperties(Stage stage, Scene scene) {
    stage.setScene(scene);
    stage.setTitle("Test");
    stage.centerOnScreen();
    stage.setResizable(true);
    ApplicationScene.settingsMenu = stage;
    Logger.log("Launcher", "Set stage properties");
}

}

This is the Main application public class ApplicationLauncher extends Application {

/**
 * The main method
 * 
 * @param args
 *            the arguments given upon start
 */
public static void main(String args[]) {
    launch(args);
}

@Override
public void start(Stage stage) throws Exception {
    Logger.log("Launcher", "Starting up..");
    Parent root = FXMLLoader.load(getClass().getResource(
            "ah.fxml"));
    Directory directory = new Directory();
//  Logger.logError("LOL", "ERROR! LOLOLOL L /n LOL \n LOL LOL");
    Scene scene = new Scene(root);
    directory.createFolder();
    setStageProperties(stage, scene);
    Settings.main(null);
    Logger.log("Launcher", "Application started up!");
}

/**
 * Sets the properties of the application
 * 
 * @param stage
 *            the stage's properties to set
 * @param scene
 *            the scene's properties to set
 */
private void setStageProperties(Stage stage, Scene scene) {
    stage.setScene(scene);
    stage.setTitle("Test");
    stage.centerOnScreen();
    stage.setResizable(true);
    stage.show();
    Logger.log("Launcher", "Set stage properties");
}

}

Result:

Caused by: java.lang.IllegalStateException: Application launch must not be called more than once

It's no where else called. (Btw. doing Settings.main(null); is the same as Settings.launch(); lol)

After re-thinking the concept

This did it:

new Settings().start(new Scene());
Sh0ck
  • 97
  • 2
  • 12
  • You don't need static access. Make sure the `fx:id="something"` is filled in for your `FXML` document. The "something" part must be exactly your variable name. So in your FXML you have `fx:id="destination"` and in your Controller you have `@FXML public TextField destination;`. They must match the type as well. And do not forget to make your FXML use your controller as a controller, otherwise things don't get initialized properly. – SnakeDoc Jul 27 '15 at 18:44
  • If you make the fields `static`, it definitely won't work. If it still doesn't work after correcting that, you have other errors too. Post the FXML file, otherwise we can only guess as to what those errors are (@SnakeDoc makes some good guesses, but you need to show your code to know which, if any, of those are the problem). – James_D Jul 27 '15 at 19:25
  • @SnakeDoc the FX id's are correct. – Sh0ck Jul 29 '15 at 15:31
  • **Edited the post to Show the FXML file of the opening window** – Sh0ck Jul 29 '15 at 15:36
  • @Sh0ck please paste your full Controller as well as your class file that `extends Application` or invokes the `launch()` static method. – SnakeDoc Jul 29 '15 at 16:00
  • Um? The first snippet I posted is pretty much fulfilling the concept of the launch() method, and the second is the Controller just without the class body. @SnakeDoc – Sh0ck Jul 29 '15 at 16:32
  • Don't try to "fulfill" the concept of the launch() method. The `launch()` method takes a new instance of your extended Application class and loads it onto the JavaFX UI thread. By bypassing it, you are not initializing your UI properly, and hence, you are trying to modify elements which are either null, or controlled from another thread, leading to no visible changes. Here is an example of how to do it proper: https://github.com/MoneyBeets/Narvaro/blob/master/src/java/edu/csus/ecs/moneybeets/narvaro/Narvaro.java#L81 – SnakeDoc Jul 29 '15 at 17:22
  • Why do you have `public static void start() { Application.launch(); }` ? The `launch()` is generally called from the `main()` or not called at all. – ItachiUchiha Jul 29 '15 at 19:17
  • @ItachiUchiha that particular program uses a separate classloader, which invokes that method. Not exactly what the OP is doing, but wanted to demonstrate it's use. – SnakeDoc Jul 29 '15 at 19:43
  • You don't want to call the `launch()` method every time a button is clicked, hence the stack trace you are seeing. The `launch()` method will take the current class and load it on the UI thread. – SnakeDoc Jul 29 '15 at 19:53
  • It seems your `Settings` class is a controller for a different FXML than your main application, and is displayed when a button is pressed. If so, then I would move your logic to start the Settings menu out of the Settings.class and into your main application controller. Then from your main application controller, you have a handler method which takes the actionevent from the button and invokes the methods to display the setting page. Then this leaves your `Settings.class` to be just a normal controller for buttons on the settings menu (it does not need to extend Application then, etc). – SnakeDoc Jul 29 '15 at 19:55

2 Answers2

1

After a long time of discussing with @SnakeDoc, We finally managed to fix it.

For anyone who had the same problem:

new Settings().start(new Scene());

This does it. Since the start method basically does everything (Loads the FXML etc.), only that line of code will be needed.

If you're still having difficulties, make sure the FXID is identical with the variable name of the instance to connect with.

Sh0ck
  • 97
  • 2
  • 12
0

This is an example of some logic embedded in a main application controller:

// somewhere in the declarations for the controller class
private Parent webViewRoot;
private Stage webViewStage;
private Scene webViewScene;
private Path webViewFXML;

// somewhere in the logic section of the controller class
public void handleToggleWebView(final ActionEvent event) {
    initWebView();
    if (toggleWebView.isSelected()) {
        webViewStage.show();
    } else {
        webViewStage.close();
    }
}

private void initWebView() {
    if (webViewFXML == null) {
        webViewFXML = Paths.get(ConfigurationManager.ProdFeature.getHomeDirectory() 
                + File.separator + "resources" + File.separator + "WebView.fxml");
    }
    if (webViewRoot == null) {
        try {
            final FXMLLoader fxmlloader = new FXMLLoader(webViewFXML.toUri().toURL());
            fxmlloader.setRoot(webViewRoot);
            fxmlloader.setController(WebViewController.getInstance());
            webViewRoot = fxmlloader.load();
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            final Dialog exceptionDialog = new Dialog.Builder(DialogType.EXCEPTION)
            .setTitle("ERROR").setHeaderText("Failed to load WebView FXML.")
            .setException(e).build();
            exceptionDialog.showAndWait();
            return;
        }
    }
    if (webViewScene == null) {
        webViewScene = new Scene(webViewRoot);
    }
    if (webViewStage == null) {
        webViewStage = new Stage();
        try {
            // add icon
            Path icon = Paths.get(ConfigurationManager.ProdFeature.getHomeDirectory() 
                    + File.separator + "resources" + File.separator + "logo.png");
            webViewStage.getIcons().add(new Image(icon.toUri().toURL().toExternalForm()));
        } catch (Exception e) {
            // ignore
        }

        StringBuilder title = new StringBuilder();
        title.append(ConfigurationManager.ProdFeature.getString("prodfeature.program.name"));
        title.append(" WebView | version ");
        title.append(ConfigurationManager.ProdFeature.getString("prodfeature.program.version"));
        webViewStage.setTitle(title.toString());
        webViewStage.setScene(webViewScene);
        webViewStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(final WindowEvent we) {
                we.consume();
                toggleWebView.setSelected(false);
                webViewStage.close();
            }
        });
        webViewStage.initOwner(productTitleLabel.getParent().getScene().getWindow());
    }
}

In this example, all this logic is in your main controller class. When the Button toggleWebView; button is pressed, it calls the handleToggleWebView() method (setup in the FXML as the onAction). This in turn loads the new window if it's not already constructed, then displays or hides it. The new window has it's own FXML file, and it's own controller, which is then used to handle button click events, etc... for that window only.

SnakeDoc
  • 13,611
  • 17
  • 65
  • 97
  • So you want me to load it at first, then if the button is clicked either hide or show the GUI? @SnakeDoc – Sh0ck Jul 29 '15 at 20:15
  • @Sh0ck if that works for your application, you can take a lot of the code above and adapt it slightly to fit your needs. You don't necessarily need to load it at application startup, but you can if you want. The above code is a sort of "lazy" loader for the additional page.. ie. it does not create the page in memory/load it until the button is first clicked. But then after it's been clicked once, it keeps it alive in memory in case the user clicks the button again (saves time re-loading the page, etc). – SnakeDoc Jul 29 '15 at 20:27
  • And oh yeah, no, Settings.class is pretty much the class the application is being initialized, I'm using seperate classes for the actual controler, for instance: HospitalLauncher = The initializer / Application, HospitalScene = the controler – Sh0ck Jul 29 '15 at 20:33
  • Ah, I see. In that case, take a look at this simpler example: https://github.com/SnakeDoc/superD/blob/master/src/com/vanomaly/superd/Main.java#L23 You will notice the `main()` method calls the static `launch()` method, which launches the JavaFX UI thread and platform, and then invokes the `start()` method automatically. In the `start()` method, you will do your FXML loading and eventually call `show()` on your stage to display your UI. In your controller, you would just have the glue logic to handle button clicks and stuff. – SnakeDoc Jul 29 '15 at 20:38
  • Just tried that. Doesn't work. Still Caused by: java.lang.IllegalStateException: Application launch must not be called more than once . What I did was; I'm calling launch() of Settings instance in the Main Controler **initializer**, not the controler itself. Then, I created a field, well, Stage instance in the **controler**, and assigned the Stage as value to the field. Then I checked it if it's null or not, if not it'll do show(), if so it'll call Settings.launch() again. Still didn't work out, this is a big problem, I'm about to collapse haha – Sh0ck Jul 29 '15 at 20:52
  • only ever call the `launch()` once, it copies the class into the JavaFX UI thread and re-instantiates it. So you created a circular initialization, which is what is causing the exception to be thrown. Don'y call `launch()` anywhere except from your `public static void main(String[] args) {}` method, and only do it a single time. Your `initialize()` method in your Controller will be automatically called *after* the `launch()` method has completed and finished executing the `start()` method automatically. – SnakeDoc Jul 29 '15 at 20:56
  • So it's `YourMainClass.main()` --> `Application.launch()` --> `YourMainClass.start()` --> `YourController.initialize()`. The `start()` and `initialize()` methods are automatically called after `launch()` has been called (and assuming no errors during loading of your FXML. – SnakeDoc Jul 29 '15 at 20:57
  • Okay, edited the topic, check how I did it @SnakeDoc – Sh0ck Jul 29 '15 at 21:10
  • @Sh0ck remove the `Settings.main(null);` line from your `start()` method. As-is, it's a circular initialization. You app calls `main()`, which in turn calls `launch()`, which automatically calls `start()` which due to how you coded it currently, calls `main()` again, which calls `launch()` again, which calls `start()` again which calls `main()` again, etc... – SnakeDoc Jul 29 '15 at 22:11
  • Sorry for the late answer, laptop was getting repaired, now everything works fine again (laptop itself). To the query itself: What now? I removed it, yet there is no reaction. @SnakeDoc – Sh0ck Aug 12 '15 at 09:38
  • @Sh0ck Ah, that'll do it. You can put an answer to your own question and mark it as the correct answer. You never know, might help someone in the future. – SnakeDoc Aug 12 '15 at 14:39