【问题标题】:JavaFX Pop Up window throws exception when called again再次调用 JavaFX 弹出窗口时抛出异常
【发布时间】:2021-06-12 19:30:40
【问题描述】:

我目前正在尝试使用 JavaFX 在 Java 中编写一个简单的应用程序。

在应用程序中,我希望有一个弹出窗口提示用户输入。只要用户不尝试再次打开弹出窗口,这工作正常。 如果他这样做,则会发生以下错误:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Grid hgap=5.0, vgap=0.0, alignment=TOP_LEFTis already set as root of another scene

这是打开窗口的代码:

在主视图控制器中:

btAddChat.setOnAction(e -> lvItems.add(PopUpVC.display()));

在视图控制器中弹出:

public class PopUpVC
{
    private static final GridPane root = new GridPane();
    private static final TextField tfInput = new TextField();
    private static final Button btOK = new Button("OK");
    private static String result;
    private static Stage primaryStage = new Stage();


public static String display()
{
    Scene scene = new Scene(root, 200, 50);
    MainVC mvc = new MainVC();
    root.setPadding(new Insets(10));

    root.setHgap(5);

    tfInput.setPrefWidth(scene.getWidth()*0.65);

    root.add(btOK, 0, 0, 1, 1);
    root.add(tfInput, 1, 0, 1, 1);

    btOK.setOnAction(e ->
    {
        if(!tfInput.getText().equals(""))
        {
            primaryStage.close();
        }
    });

    primaryStage.setResizable(false);
    primaryStage.setScene(scene);
    primaryStage.showAndWait();
    return tfInput.getText();
}

我只复制了最重要的部分,实际错误要长得多。我知道是什么导致了错误(尝试打开具有相同根目录的窗口会引发错误,因为根目录已在使用中),我只是不知道如何解决它。

这是应用程序的图片:

左上角的按钮打开弹出窗口。

弹出窗口:

如果有关于如何解决此问题的任何建议,我会很高兴听到他们,如果应该有任何其他方式来打开提示用户输入文本的弹出窗口,我也很乐意听说这些!

提前非常感谢!

【问题讨论】:

  • 错误信息是不言自明的:GridPane 已经是一个场景的根,而您尝试使其成为另一个场景的根。制作static 几乎总是一个错误(或至少是糟糕的设计),但无论哪种方式,GridPane 都应该是单个场景的根源。
  • 我想通了,真的很简单,我在启动函数中添加了一个参数,所以当我调用它时,我只需给它一个新的 GridPane() 就可以了。
  • 也可以考虑使用TextInputDialog
  • @James_D 感谢您的评论,什么时候做一些静态的东西是个好主意,我还在学习,很高兴得到一些建议!
  • "什么时候将某些东西设为静态是个好主意"。几乎从来没有......在工厂模式中static 方法有一些很好的用例,但在除了相当高级的编码风格之外,唯一应该是静态的东西是常量和main() 方法。

标签: java exception javafx error-handling


【解决方案1】:

OP的解决方案:

我想通了,真的很简单,我在 start 函数中添加了一个参数,所以当我调用它时,我只需给它一个 new GridPane() 就可以了。

确实是错误的做法。正如@James_D 指出的那样,static 对于这样的事情不是一个好主意。为了尽可能地保持原始设计,我建议这样做,它只构建一次 PopUp,然后重新显示它:

public class PopUp extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Click Me");
        PopUpVC popUpVC = new PopUpVC();
        button.setOnAction(evt -> {
            popUpVC.display();
        });
        primaryStage.setScene(new Scene(button));
        primaryStage.show();
    }

    public class PopUpVC {
        private final TextField tfInput = new TextField();
        private Stage primaryStage = new Stage();


        public PopUpVC() {
            GridPane root = new GridPane();
            Scene scene = new Scene(root, 200, 50);
            root.setPadding(new Insets(10));
            root.setHgap(5);
            tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
            Button btOK = new Button("OK");
            root.add(btOK, 0, 0, 1, 1);
            root.add(tfInput, 1, 0, 1, 1);
            btOK.setOnAction(e -> {
                if (!tfInput.getText().equals("")) {
                    primaryStage.close();
                }
            });
            primaryStage.setResizable(false);
            primaryStage.setScene(scene);
        }

        public String display() {
            primaryStage.showAndWait();
            return tfInput.getText();
        }
    }
}

当您拥有完全静态的东西时,例如PopUpVC,这暗示它只是您从其他地方移出的代码。如果您从各种其他类调用方法,这很有用,也是一个很好的做法,因为它可以节省代码重复。但是,您不应该在此类中将 JavaFX 元素作为静态字段。

在这种情况下,您可以取消 PopUpVC 并将所有代码移动到本地方法中:

public class PopUp extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Click Me");
        button.setOnAction(evt -> {
            displayPopUpVC();
        });
        primaryStage.setScene(new Scene(button));
        primaryStage.show();
    }

    public String displayPopUpVC() {
        TextField tfInput = new TextField();
        Stage primaryStage = new Stage();
        GridPane root = new GridPane();
        Scene scene = new Scene(root, 200, 50);
        root.setPadding(new Insets(10));
        root.setHgap(5);
        tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
        Button btOK = new Button("OK");
        root.add(btOK, 0, 0, 1, 1);
        root.add(tfInput, 1, 0, 1, 1);
        btOK.setOnAction(e -> {
            if (!tfInput.getText().equals("")) {
                primaryStage.close();
            }
        });
        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.showAndWait();
        return tfInput.getText();
    }
}

这与第一个解决方案的行为略有不同,因为它不会在两次调用之间保留 TextField 中的文本。但是,如果您愿意,可以在参数中传递默认文本。

但是,正如@James_D 所指出的,TextInputDialog 完全符合您的要求。另外,它只需要大约 4 行代码即可完成:

public class PopUp extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Click Me");
        TextInputDialog dialog = new TextInputDialog();
        dialog.setHeaderText(null);
        dialog.setTitle("Chat");
        dialog.setGraphic(null);
        button.setOnAction(evt -> {
            dialog.showAndWait();
        });
        primaryStage.setScene(new Scene(button));
        primaryStage.show();
    }
}

【讨论】:

  • 感谢您为答案付出了这么多,这真的帮助了我!如果我在作业中使用它,你可以吗?如果不是这样也没关系,我会从你的方法中获得一些灵感。
  • 随心所欲地使用它,只要你能学到一些东西。
猜你喜欢
  • 1970-01-01
  • 2019-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多