简体   繁体   English

JavaFX - 中心子阶段到父阶段

[英]JavaFX - Center Child Stage to Parent Stage

I'm new in JavaFX and I have problem with setting position of my new window, related to parent window position. 我是JavaFX的新手,我在设置新窗口的位置方面遇到问题,与父窗口位置有关。 I want to open new window at the center of parent window. 我想在父窗口的中心打开新窗口。

I tried these two ways: 我试过这两种方式:

1) Init owner - i thought that init owner will open my window at the center of parent but it doesn't 1)初始所有者 - 我认为init所有者将在父母的中心打开我的窗口,但事实并非如此

FXMLLoader fxmlLoader = new     FXMLLoader(AddUserController.class.getResource("/view/addUserStage.fxml"));
Parent root = fxmlLoader.load();
Stage stage = new Stage();
stage.initOwner(parentStage);
stage.showAndWait();

2) Reposition when windows is shown - it works but after window is shown I can see its first position and then I see it is moved to new calculated position. 2)当窗口显示时重新定位 - 它工作但是在显示窗口后我可以看到它的第一个位置,然后我看到它被移动到新的计算位置。 I can't accept that. 我不能接受。

FXMLLoader fxmlLoader = new     FXMLLoader(AddUserController.class.getResource("/view/addUserStage.fxml"));
Parent root = fxmlLoader.load();
Stage stage = new Stage();
stage.initOwner(parentStage);
stage.setOnShown(event -> controller.onWindowShown());
stage.showAndWait();

In onWindowShown function I'm setting new X and Y position of my window, because only after shown width and height of my window are known and i can easly calculate new position. 在onWindowShown函数中,我正在设置窗口的新X和Y位置,因为只有在显示窗口的宽度和高度之后,我才能轻松计算新位置。

How can I can set child window position at the center of parent window before showAndWait function? 如何在showAndWait函数之前将子窗口位置设置在父窗口的中心?

As the other answer correctly states, JavaFX calculates the stage width and height as the window is being shown. 正如另一个答案所正确指出的那样,JavaFX会在显示窗口时计算舞台宽度和高度。 The onShowing() property is called before the calculation takes place, and before the window is set visible, so placing a stage.hide() command there effectively does nothing. onShowing()属性在计算发生之前被调用,并且在窗口设置为可见之前,因此将stage.hide()命令放在那里实际上什么都不做。 And as the question states, seeing a blip on the screen just before the window is relocated is a less-than-desirable outcome. 正如问题所述,在窗口重新定位之前看到屏幕上的光点是一个不太理想的结果。

The only way I have found to solve this problem (though I would love to know if a better way exists) is to add Listeners to the width and height properties of the stage, and let them fire the event to change the window's position. 我找到解决这个问题的唯一方法(尽管我很想知道是否存在更好的方法)是将监听器添加到舞台的宽度和高度属性中,然后让它们触发事件以更改窗口的位置。 Then once the window is visible, remove the Listeners. 然后,一旦窗口可见,请删除监听器。

for example: 例如:

//Let's say we want to center the new window in the parent window

ChangeListener<Number> widthListener = (observable, oldValue, newValue) -> {
        double stageWidth = newValue.doubleValue();
        stage.setX(parentStage.getX() + parentStage.getWidth() / 2 - stageWidth / 2);
};
ChangeListener<Number> heightListener = (observable, oldValue, newValue) -> {
        double stageHeight = newValue.doubleValue();
        stage.setY(parentStage.getY() + parentStage.getHeight() / 2 - stageHeight / 2);   
};

stage.widthProperty().addListener(widthListener);
stage.heightProperty().addListener(heightListener);

//Once the window is visible, remove the listeners
stage.setOnShown(e -> {
    stage.widthProperty().removeListener(widthListener);
    stage.heightProperty().removeListener(heightListener);
});

stage.show();

If you want to show a Stage you have to put the content loaded from the FXML file to a Scene and this Scene will be set on the Stage . 如果要显示Stage ,则必须将从FXML文件加载的内容放到Scene ,此Scene将在Stage上设置。

Unfortunately the size of the Stage is unknown until it is rendered (as you mentioned), but to relocate it to the center of the parent Stage , you have to know the size. 不幸的是, Stage的大小在呈现之前是未知的(如你所提到的),但要将其重新定位到父Stage的中心,你必须知道大小。

One trick that could work is to hide the Stage before it is shown, relocate it, and then make it visible again on the new position. 一个可行的技巧是在显示Stage之前隐藏 Stage ,重新定位它,然后在新位置再次显示它

Example

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root, 400, 400);

            Button button = new Button("Open another Stage");
            button.setOnAction(e -> {

                Stage popUpStage = new Stage();

                // Here add the FXML content to the Scene
                Scene popUpScene = new Scene(new Button());
                popUpStage.setScene(popUpScene);

                // Calculate the center position of the parent Stage
                double centerXPosition = primaryStage.getX() + primaryStage.getWidth()/2d;
                double centerYPosition = primaryStage.getY() + primaryStage.getHeight()/2d;

                // Hide the pop-up stage before it is shown and becomes relocated
                popUpStage.setOnShowing(ev -> popUpStage.hide());

                // Relocate the pop-up Stage
                popUpStage.setOnShown(ev -> {
                    popUpStage.setX(centerXPosition - popUpStage.getWidth()/2d);
                    popUpStage.setY(centerYPosition - popUpStage.getHeight()/2d);
                    popUpStage.show();
                });

                popUpStage.showAndWait();
            });

            root.setCenter(button);


            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

You could layout the Scene before showing the Stage and determine the position based on the Scene size. 您可以在显示Stage之前布局Scene ,并根据Scene大小确定位置。 This will not take the window decorations into account however, so the position could be slightly off unless you have a window with the same kind of decorations to take the size of those decorations into account: 然而,这不会考虑窗户装饰,所以位置可能稍微偏离,除非你有一个具有相同装饰的窗口,以考虑这些装饰的大小:

@Override
public void start(Stage primaryStage) {
    Button btnMain = new Button("show");
    btnMain.setOnAction(evt -> {
        Stage stage = new Stage();
        Button btn = new Button("Close");
        btn.setOnAction((ActionEvent event) -> {
            stage.close();
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Bounds mainBounds = primaryStage.getScene().getRoot().getLayoutBounds();
        Scene scene = new Scene(root, 400, 400);

        stage.setScene(scene);

        // layout new scene
        scene.getRoot().applyCss();
        scene.getRoot().layout();

        Bounds rootBounds = scene.getRoot().getLayoutBounds();
        stage.setX(primaryStage.getX() + (mainBounds.getWidth() - rootBounds.getWidth()) / 2);
        stage.setY(primaryStage.getY() + (mainBounds.getHeight() - rootBounds.getHeight()) / 2);
        stage.showAndWait();
        System.out.println("done");
    });
    Scene mainScene = new Scene(new StackPane(btnMain), 600, 600);
    primaryStage.setScene(mainScene);
    primaryStage.show();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM