JavaFX开辟了新局面 [英] JavaFX open a new scene
问题描述
我希望我的代码能够正常工作,以便当我单击一个按钮时,会打开一个新场景,但是它不起作用,我也不知道为什么.
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();
}
}
GUI成功显示,但是当我按下Button时,它会引发异常.
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;
}
}
更新
我尝试将应用程序类与控制器类分开,如下面的James_D的答案所示,但出现以下异常:
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
推荐答案
您似乎正在尝试将主应用程序类用作控制器类.这将造成混乱,您应该避免这种情况.这是发生了什么:
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:
启动应用程序时,它将调用launch(...)
.从Application
继承的launch
方法将执行许多重要的内务处理",例如启动JavaFX工具包和JavaFX Application Thread.然后,它将创建您的应用程序子类的实例,创建初始的Stage
并在该实例中调用start(...)
.
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.
在start(...)
方法中,您初始化了几个实例变量(window
和openScene
),并加载了一个FXML文件,以显示其内容. FXMLLoader.load(...)
方法执行以下操作:
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:
- 基于文件中的XML元素创建UI元素
- 在FXML文件中创建由
fx:controller
属性指定的类的实例 - 将所有带有
@FXML
注释的字段注入到控制器实例中 - 注册FXML文件中指定的所有事件处理程序
- 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
请注意,如果为应用程序和控制器指定相同的类,则将创建该类的两个实例.一种由launch
方法创建,而另一种由FXMLLoader
方法创建.请注意,只有launch
方法创建的实例才调用start(...)
方法.由于您是在start(...)
方法中初始化实例变量的,因此这些变量不会在FXMLLoader
创建的实例中初始化.因此,在由FXMLLoader
创建的实例中(如果需要,可以称为控制器实例"),不会初始化window
和openScene
.因此,线
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);
将抛出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();
因此无需缓存Stage
实例.
所以:
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);
}
}
还请注意,您的OpenScene
类无需成为Application
子类:每个应用程序只需要一个这样的类:
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();
}
}
这篇关于JavaFX开辟了新局面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!