Issue
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
Solution
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();
}
}
Answered By - James_D
Answer Checked By - Marilyn (JavaFixing Volunteer)