JavaFX Pop Up window 再次调用时抛出异常

JavaFX Pop Up window throws exception when called again

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

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

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

这是打开 window 的代码:

在主视图控制器中:

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

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

这是应用程序的图片:

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

弹窗:

如果有任何关于如何解决此问题的建议,我将很高兴听到他们的意见,如果有任何其他方法可以打开提示用户输入文本的弹出窗口 window,我也很高兴听到这些!

非常感谢!

OP的解决方案:

I figured it out, easy thing really, I added a parameter to the start function so when I call it I just give it a new GridPane() and it works perfectly fine.

真是错误的做法。正如@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,这暗示它只是你从其他地方移出的代码。如果您从各种其他 classes 调用方法,那么这很有用,也是一种很好的做法,这样可以节省代码重复。但是,您不应该在 class.

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