【问题标题】:How to switch scenes in JavaFX如何在 JavaFX 中切换场景
【发布时间】:2016-09-09 02:38:37
【问题描述】:

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

我有一个计算器,我的目标是选择一个菜单选项来更改计算器(即:基本和科学)。现在我只是在测试,所以这是我到目前为止与这个问题相关的代码(我正在使用 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();
    }

}

【问题讨论】:

    标签: java javafx fxml scenebuilder scene


    【解决方案1】:

    我编写了一个库,使切换场景变得非常简单。您只需为 Integer 类型的每个场景选择一个唯一 ID,然后使用一行代码添加场景。当您需要显示场景时,您可以从项目中的任何类中调用该方法并将场景的 ID 传递给它。

    You can find the library here 以及 Maven 导入 xml。

    【讨论】:

      【解决方案2】:

      我刚刚遇到了同样的问题,this answer 完美地解决了我的问题,同时又短又干净。

      @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()));
      
      }
      

      【讨论】:

        【解决方案3】:
        <?xml version="1.0" encoding="UTF-8"?>
        <?import javafx.scene.image.*?>
         <?import javafx.scene.text.*?>
         <?import java.lang.*?>
         <?import java.util.*?>
        <?import javafx.scene.*?>
        <?import javafx.scene.control.*?>
        <?import javafx.scene.layout.*?>
        <AnchorPane id="AnchorPane" prefHeight="491.0" prefWidth="386.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="todoapp.TypesController">
        <children>
          <CheckBox fx:id="c1" layoutX="55.0" layoutY="125.0" mnemonicParsing="false" onAction="#clicked" text="ADD dcu" />
          <CheckBox fx:id="c2" layoutX="55.0" layoutY="177.0" mnemonicParsing="false" onAction="#clicked1" text="Display dcu" />
          <Label layoutX="31.0" layoutY="58.0" prefHeight="37.0" prefWidth="276.0" text="Choose any one of the options" textFill="#1b29cd">
             <font>
                <Font name="Arial Bold" size="18.0" />
             </font>
          </Label>
        </children>
        

        【讨论】:

        • 请编辑您的第一个答案并添加其他答案中的 sn-ps(并解释他们如何/为什么按照评论中的建议解决问题)
        • 只是让你知道我在 2 年前发布了这个。我想我所做的只是从答案中获取建议,在谷歌上进行了更多搜索,然后得出了答案。我对编程还是很陌生,不知道自己在做什么
        【解决方案4】:
        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
          } }
        

        【讨论】:

        • 用这么长的代码示例做一些解释会很酷。
        • @ChristianM 我喜欢你对这个答案给出反馈的方式..:-)
        【解决方案5】:

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

        public class ScreenController {
            private HashMap<String, Pane> 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 全屏过渡。

        【讨论】:

          【解决方案6】:

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

          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);
          }
          

          【讨论】:

          • 我已经尝试过了,但我不断收到空指针异常。在得到两个关于同一件事的 cmet 之后,我决定发布我的异常消息,见上文。
          • 获得NullPointerException的那一行的代码是什么?
          • 上面我已经添加了我得到的异常,底部有注释。但是我在不使用 Scene Builder 的情况下厌倦了几个类似的例子,它工作得非常好,所以它肯定是 Scene Builder 的东西
          • start方法中作为参数传递的阶段是否有可能没有分配给成员primaryStage?这将导致NullPointerException 在线primaryStage.getScene().setRoot(pane)。我更新了代码示例以包含 start 方法的片段。
          • 对不起,我知道这是一个旧帖子。你为什么要this.primaryStage = primaryStage;?为什么不直接忽略 start() 方法参数并使用 this.primaryStage 做所有事情?
          【解决方案7】:

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

          【讨论】:

          • 我已经尝试过了,但我不断收到空指针异常。在得到两个关于同一件事的 cmet 之后,我决定发布我的异常消息,见上文。
          • 在您的代码中,您混合了 Application 和 FXML 控制器概念。每个都有不同的初始化生命周期。 FXML 控制器基于依赖注入原理,初始化应在init 方法中完成。阅读 FXML 控制器。也可以看下面stackoverflow.com/questions/34785417/…
          • 那么我该如何切换场景。我尝试使用 init 方法并设置一个字符串参数。如果我传入一个诸如“test”之类的字符串并使用 syso 打印出结果,它将打印出 test。但是如果我用 FXML 文件传入一个字符串并尝试设置一个场景,它会给我一个空指针异常
          猜你喜欢
          • 2016-04-13
          • 1970-01-01
          • 2020-03-23
          • 1970-01-01
          • 1970-01-01
          • 2016-09-09
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          相关资源
          最近更新 更多