4

I want my code to work such that when I click on a Button, a new scene opens, but it doesn't work and I don't know why.

public void start(Stage primaryStage) throws Exception {
    window = primaryStage;

    Parent root = FXMLLoader.load(getClass().getResource("FXML/LoginScene.fxml"));
    scene = new Scene(root,400,400);
    openScene = new OpenScene(writer);
    window.setScene(scene);
    window.show();


}
public static void main(String[] args){
    launch(args);
}
@FXML protected void btnConnect(ActionEvent event) {
    System.out.println("hallo");
    try {
        openScene.start(window);
    } catch (Exception e) {

        e.printStackTrace();
    }
}

The GUI successfully shows up, but when I press the Button, it throws an Exception.

public class OpenScene extends Application{
    PrintWriter writer;

    @Override
    public void start(Stage window) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXML/OpenScene.fxml"));
        Scene scene =  new Scene(root, 200 ,200);
        window.setScene(scene);
        window.show();

    }
    public OpenScene(PrintWriter writer){
        this.writer = writer;
    }
}

Update

I tried to separate the application class from the controller class as in James_D's answer below, but I got the following exception:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1762)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1645)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8216)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3724)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3452)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1728)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2461)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:348)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:273)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:382)
at com.sun.glass.ui.View.handleMouseEvent(View.java:553)
at com.sun.glass.ui.View.notifyMouse(View.java:925)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1759)
... 43 more
Caused by: java.lang.NullPointerException
at MainController.btnConnect(MainController.java:22)
... 53 more
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
  • Are you trying to use your application class as the controller class? These really should be separate classes. Also, as mentioned previously, you really should only have one `Application` subclass per application. (You can just take the `extends Application` and `@Override` out of your `OpenScene` class, no?) And finally, please post the stack trace from the Exception. – James_D Mar 16 '15 at 15:43
  • why is this so important? –  Mar 16 '15 at 15:44
  • Because you will have two instances of your class. One that was created by `launch`, which calls `start(...)` for you, and one that was created by the `FXMLLoader`. Only the first one will have initialized the `window` field. – James_D Mar 16 '15 at 15:46
  • this does not change anything –  Mar 16 '15 at 16:12
  • So what is on line 22 of `MainController`? (Maybe just edit the question to include that class and identify line 22.) – James_D Mar 16 '15 at 16:53

1 Answers1

10

It looks like you are trying to use your main application class as the controller class. This is going to be confusing, and you should avoid it. Here is what happens:

When you start up the application, it calls launch(...). The launch method, inherited from Application, will do a bunch of important "housekeeping", such as starting the JavaFX toolkit and the JavaFX Application Thread. It then creates an instance of your application subclass, creates an initial Stage and calls the start(...) in that instance.

In your start(...) method, you initialized a couple of instance variables (window and openScene), and loaded an FXML file, displaying its contents. The FXMLLoader.load(...) method does the following:

  • Creates UI elements based on the XML elements in the file
  • Creates an instance of the class specified by the fx:controller attribute in the FXML file
  • Injects any @FXML-annotated fields into the controller instance
  • Registers any event handlers specified in the FXML file

Notice that if you specify the same class for the application and for the controller, two instances of that class will be created. One is created by the launch method, and one is created by the FXMLLoader. Note that only the instance created by the launch method has had the start(...) method invoked. Since you initialize the instance variables in the start(...) method, those variables are not initialized in the instance created by the FXMLLoader. So in the instance created by the FXMLLoader (the "controller instance", if you like), window and openScene are not initialized. Hence the line

    openScene.start(window);

will throw a NullPointerException.

Since the application and controller really have completely different roles, you should separate them into different classes. This will make things far less confusing. Note that you can always find the window in which a node is displayed by calling

anyNode.getScene().getWindow();

so there is no need to cache the Stage instance.

So:

public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        // change LoginScene.fxml so it now has fx:controller="LoginController"
        Parent root = FXMLLoader.load(getClass().getResource("FXML/LoginScene.fxml"));

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

and use a different class for the controller:

public class LoginController {

    private OpenScene openScene ;

    @FXML
    private Button connectButton ; // needs fx:id in fxml file...

    public void initialize() throws Exception {
        PrintWriter writer = ... ;
        openScene = new OpenScene(writer);
    }

    @FXML // handler for connect button:
    private void btnConnect() throws Exception {
        Stage stage = (Stage) connectButton.getScene().getWindow();
        openScene.start(stage);
    }
}

Note also that there is no need for your OpenScene class to be an Application subclass: you only need one such class per application:

public class OpenScene {

    private final PrintWriter writer ;

    public OpenScene(PrintWriter writer) {
        this.writer = writer ;
    }

    // doesn't need to be called "start" any more...    
    public void start(Stage window) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXML/OpenScene.fxml"));
        Scene scene =  new Scene(root, 200 ,200);
        window.setScene(scene);
        window.show();
    }
}   
James_D
  • 201,275
  • 16
  • 291
  • 322
  • hi i did it like you; but i throughs an Exception –  Mar 16 '15 at 16:40
  • 2
    What is the exception? Which line is it thrown from? No-one can possibly help you if all you keep saying it "It throws an exception." – James_D Mar 16 '15 at 16:42