如何在JavaFX中切换场景

我看了很多页面试图找出如何切换场景,但我没有成功。

我有一个计算器,我的目标是选择一个菜单选项来改变计算器(即:基本和科学)。 现在我只是测试所以这里是我的代码到目前为止这个问题(我使用的是Scene Builder):

@FXML private MenuItem basic; @FXML private MenuItem testSwitch; public static void main(String[] args) { Application.launch( args ); } @Override public void start(Stage primaryStage) throws Exception { Parent pane = FXMLLoader.load( getClass().getResource( "calculator.fxml" ) ); Scene scene = new Scene( pane ); primaryStage.setScene(scene); primaryStage.setTitle( "Calculator" ); primaryStage.show(); } @FXML public void handleMenuOption(ActionEvent e) { if(e.getSource()==basic) { changeScene("calculator.fxml"); } else if(e.getSource()==testSwitch) { changeScene("TestSwitch.fxml"); } } public void changeScene(String fxml) { //this prints out System.out.println(fxml); } 

编辑我已经尝试了很多东西。 无论如何,我总是得到这个NullPointerException。 我觉得它可能与在场景构建器中设置某些内容有关,但我一直无法找到答案

 Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source) at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source) at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.event.Event.fireEvent(Unknown Source) at javafx.scene.control.MenuItem.fire(Unknown Source) at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source) at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(Unknown Source) at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source) at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) at javafx.event.Event.fireEvent(Unknown Source) at javafx.scene.Scene$MouseHandler.process(Unknown Source) at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source) at javafx.scene.Scene.impl_processMouseEvent(Unknown Source) at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(Unknown Source) at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source) at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source) at com.sun.glass.ui.View.handleMouseEvent(Unknown Source) at com.sun.glass.ui.View.notifyMouse(Unknown Source) at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source) at java.lang.Thread.run(Unknown Source) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.reflect.misc.Trampoline.invoke(Unknown Source) at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.reflect.misc.MethodUtil.invoke(Unknown Source) ... 44 more Caused by: java.lang.NullPointerException at CalculatorMain.changeScene(CalculatorMain.java:75) at CalculatorMain.handleMenuOption(CalculatorMain.java:64) ... 53 more at CalculatorMain.changeScene(CalculatorMain.java:75) This is at:stage . getScene() . setRoot(pane); at CalculatorMain.handleMenuOption(CalculatorMain.java:64) This is at:changeScene ("TestSwitch.fxml"); 

工作代码:

我玩了下面使用下面的建议,并使用此代码使其工作:

 private Stage stage; public static void main(String[] args) { Application.launch( args ); } @Override public void start(Stage primaryStage) throws Exception { this.stage = primaryStage; FXMLLoader loader = new FXMLLoader(getClass() .getResource("calculator.fxml")); Parent root = (Parent)loader.load(); BasicCalculatorView controller = (BasicCalculatorView)loader.getController(); controller.setModel(new BasicCalculatorModelTest(controller)); controller.setLogic(this); primaryStage.setTitle("Calculator"); primaryStage.setScene(new Scene(root)); primaryStage.show(); } public void switchScene(String fxmlFile) { FXMLLoader loader = new FXMLLoader(getClass() .getResource(fxmlFile)); Parent root; try { root = (Parent)loader.load(); if(fxmlFile.equals("calculator.fxml")) { BasicCalculatorView controller = (BasicCalculatorView)loader.getController(); controller.setModel(new BasicCalculatorModelTest(controller)); controller.setLogic(this); } else if(fxmlFile.equals("TestSwitch.fxml")) { TestSwitch controller = (TestSwitch)loader.getController(); controller.setLogic(this); } this.stage.setScene(new Scene(root)); } catch (IOException e) { e.printStackTrace(); } } 

我写了这个控制器来跟踪不同的场景图。

 public class ScreenController { private HashMap screenMap = new HashMap<>(); private Scene main; public ScreenController(Scene main) { this.main = main; } protected void addScreen(String name, Pane pane){ screenMap.put(name, pane); } protected void removeScreen(String name){ screenMap.remove(name); } protected void activate(String name){ main.setRoot( screenMap.get(name) ); } } 

所以我可以写:

 ScreenController screenController = new ScreenController(scene); screenController.add("calculator", FXMLLoader.load(getClass().getResource( "calculator.fxml" ))); screenController.add("testSwitch", FXMLLoader.load(getClass().getResource( "TestSwitch.fxml" ))); screenController.activate("calculator"); 

这是全屏应用程序的解决方法,每次舞台切换场景时都会显示MacOS全屏转换。

而不是切换Scenes ,在已存在的Scene上切换根节点

如果你想改变场景你会这样做(注意舞台是应用程序的成员):

 private Stage primaryStage; @Override public void start(Stage primaryStage) throws Exception { this.primaryStage = primaryStage; ... } public void changeScene(String fxml){ Parent pane = FXMLLoader.load( getClass().getResource(fxml)); Scene scene = new Scene( pane ); primaryStage.setScene(scene); } 

然而正如@Eugene_Ryzhikov已经指出的那样,只是更改现有场景的根内容是一个更好的解决方案:

 public void changeScene(String fxml){ Parent pane = FXMLLoader.load( getClass().getResource(fxml)); primaryStage.getScene().setRoot(pane); } 

似乎OP已经解决了它,但由于它仍然是开放的和未解决的,我将分享我在另一个答案中找到的解决方案。 记住要么在问题解决后选择正确答案或发布自己的解决方案,它会帮助未来的人们在同一个问题中运行(就像我一样)。

我只是在同一个问题上运行, 这个答案完美地解决了我的问题,同时又简短而干净。

 @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); label.setText("Hello World!"); //Here I want to swap the screen! Stage stageTheEventSourceNodeBelongs = (Stage) ((Node)event.getSource()).getScene().getWindow(); // OR Stage stageTheLabelBelongs = (Stage) label.getScene().getWindow(); // these two of them return the same stage // Swap screen stage.setScene(new Scene(new Pane())); } 

PS。:记得查看原始答案并进行投票。 这家伙值得……

PPS。:我不确定只是复制一个答案是可以的(而不是仅通过评论分享链接),但由于这没有正确的答案,但我决定这样做是为了可见性。

 TypesController.java package todoapp; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.CheckBox; import javafx.stage.Stage; public class TypesController implements Initializable { @FXML private CheckBox c1; @FXML private CheckBox c2; public void clicked(ActionEvent e) throws IOException { Parent home_page_parent =FXMLLoader.load(getClass().getResource("AddDcuFXML.fxml")); Scene home_page_scene = new Scene(home_page_parent); Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow(); app_stage.hide(); //optional app_stage.setScene(home_page_scene); app_stage.show(); } public void clicked1(ActionEvent e) throws IOException { Parent home_page_parent = FXMLLoader.load(getClass().getResource("AddDcuFXML.fxml")); Scene home_page_scene = new Scene(home_page_parent); Stage app_stage = (Stage) ((Node)e.getSource()).getScene().getWindow(); app_stage.hide(); //optional app_stage.setScene(home_page_scene); app_stage.show(); } @Override public void initialize(URL arg0, ResourceBundle arg1) { // TODO Auto-generated method stub } }