Dialogue de position JavaFX et scène au centre de l'écran


Les codes suivants montrent le centrage d'une boîte de dialogue et de la scène au centre de l'écran. La boîte de dialogue est censée être affichée en premier pour que l'utilisateur puisse entrer les informations de connexion. Après une connexion réussie, la fenêtre principale (étape) s'affiche. J'ai trouvé la solution de centrer la boîte de dialogue et la scène à partir de ce site Web, mais cela ne semble pas très idéal. Pour la boîte de dialogue et la scène, ils doivent d'abord être affichés avant de pouvoir calculer les coordonnées, puis le positionnement dans le centre. Cela signifie que nous pouvons voir la boîte de dialogue et la fenêtre principale se déplacer vers le centre après leur affichage. Est-il un meilleur moyen? Idéalement, ils doivent être positionnés au centre avant d'être affichés.

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;

public class Demo extends Application {

    private Stage primaryStage;
    private Dialog<String> dialog;
    private Button createUserButton = new Button("Create User");

    @Override
    public void start(Stage primaryStage) throws Exception {
        this.primaryStage = primaryStage;
        Text usersLabel = new Text("Current Users:");
        TableColumn<User, String> indexColumn = new TableColumn<User, String>("No.");
        indexColumn.setMaxWidth(1f * Integer.MAX_VALUE * 10);
        indexColumn.setCellValueFactory(p -> p.getValue().indexProperty());
        TableColumn<User, String> userNameColumn = new TableColumn<User, String>("User Name");
        userNameColumn.setMaxWidth(1f * Integer.MAX_VALUE * 60);
        userNameColumn.setCellValueFactory(p -> p.getValue().userNameProperty());
        TableColumn<User, String> roleColumn = new TableColumn<User, String>("Role");
        roleColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
        roleColumn.setCellValueFactory(p -> p.getValue().roleProperty());
        TableView<User> tableView = new TableView<User>();
        tableView.getColumns().add(indexColumn);
        tableView.getColumns().add(userNameColumn);
        tableView.getColumns().add(roleColumn);
        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        Text dummyLabel = new Text("");
        VBox leftPane = new VBox(5);
        leftPane.getChildren().addAll(usersLabel, tableView);
        VBox rightPane = new VBox(20);
        rightPane.setFillWidth(true);
        rightPane.getChildren().addAll(dummyLabel, createUserButton);
        GridPane mainPane = new GridPane();
        mainPane.setPadding(new Insets(10, 0, 0, 10));
        mainPane.setHgap(20);
        mainPane.add(leftPane, 0, 0);
        mainPane.add(rightPane, 1, 0);
        Scene scene = new Scene(mainPane);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        showDialog();
    }

    private void showDialog() {
        dialog = new Dialog<>();
        dialog.setTitle("Login");
        dialog.setHeaderText("Please enter User Name and Password to login.");
        dialog.setResizable(false);
        Label userNameLabel = new Label("User Name:");
        Label passwordLabel = new Label("Password:");
        TextField userNameField = new TextField();
        PasswordField passwordField = new PasswordField();
        GridPane grid = new GridPane();
        grid.setAlignment(Pos.CENTER);
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(20, 35, 20, 35));
        grid.add(userNameLabel, 1, 1);
        grid.add(userNameField, 2, 1);
        grid.add(passwordLabel, 1, 2);
        grid.add(passwordField, 2, 2);
        dialog.getDialogPane().setContent(grid);
        dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
        Button okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
        okButton.addEventFilter(ActionEvent.ACTION, event -> {
            createUser(userNameField.getText().trim(), passwordField.getText());
            event.consume();
        });
        dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
        Platform.runLater(() -> {
            Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
            Window window = dialog.getDialogPane().getScene().getWindow();
            window.setX((screenBounds.getWidth() - window.getWidth()) / 2);
            window.setY((screenBounds.getHeight() - window.getHeight()) / 2);
        });
        dialog.showAndWait();
    }

    private void createUser(String userName, String password) {
        dialog.getDialogPane().setDisable(true);
        dialog.getDialogPane().getScene().setCursor(Cursor.WAIT);
        Task<Boolean> task = new Task<Boolean>() {
            @Override
            public Boolean call() {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException exception) {
                }
                return Boolean.TRUE;
            }
        };
        task.setOnSucceeded(e -> {
            Boolean success = task.getValue();
            dialog.getDialogPane().setDisable(false);
            dialog.getDialogPane().getScene().setCursor(Cursor.DEFAULT);
            if (success.booleanValue()) {
                Platform.runLater(() -> {
                    dialog.close();
                    primaryStage.show();
                    Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
                    primaryStage.setX((screenBounds.getWidth() - primaryStage.getWidth()) / 2);
                    primaryStage.setY((screenBounds.getHeight() - primaryStage.getHeight()) / 2);
                });
            } else {
                Alert alert = new Alert(AlertType.ERROR);
                alert.setTitle("Login Error");
                alert.setHeaderText("Unable to login.");
                alert.showAndWait();
            }
        });
        new Thread(task).start();
    }

    public static void main(String[] arguments) {
        Application.launch(arguments);
    }

}

class User {

    private StringProperty index;

    private StringProperty userName;

    private StringProperty role;

    public String getIndex() {
        return indexProperty().get();
    }

    public StringProperty indexProperty() {
        if (index == null) {
            index = new SimpleStringProperty(this, "index");
        }
        return index;
    }

    public void setIndex(String index) {
        indexProperty().set(index);
    }

    public String getUserName() {
        return userNameProperty().get();
    }

    public StringProperty userNameProperty() {
        if (userName == null) {
            userName = new SimpleStringProperty(this, "userName");
        }
        return userName;
    }

    public void setUserName(String userName) {
        userNameProperty().set(userName);
    }

    public String getRole() {
        return roleProperty().get();
    }

    public StringProperty roleProperty() {
        if (role == null) {
            role = new SimpleStringProperty(this, "role");
        }
        return role;
    }

    public void setRole(String role) {
        roleProperty().set(role);
    }

}

Voici une solution en définissant des dimensions personnalisées sur stage et dialog. Cela fonctionne pour la scène mais cela ne fonctionne pas pour la boîte de dialogue.

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;

public class Demo extends Application {

    private Stage primaryStage;
    private Dialog<String> dialog;
    private Button createUserButton = new Button("Create User");

    @Override
    public void start(Stage primaryStage) throws Exception {
        this.primaryStage = primaryStage;
        Text usersLabel = new Text("Current Users:");
        TableColumn<User, String> indexColumn = new TableColumn<User, String>("No.");
        indexColumn.setMaxWidth(1f * Integer.MAX_VALUE * 10);
        indexColumn.setCellValueFactory(p -> p.getValue().indexProperty());
        TableColumn<User, String> userNameColumn = new TableColumn<User, String>("User Name");
        userNameColumn.setMaxWidth(1f * Integer.MAX_VALUE * 60);
        userNameColumn.setCellValueFactory(p -> p.getValue().userNameProperty());
        TableColumn<User, String> roleColumn = new TableColumn<User, String>("Role");
        roleColumn.setMaxWidth(1f * Integer.MAX_VALUE * 30);
        roleColumn.setCellValueFactory(p -> p.getValue().roleProperty());
        TableView<User> tableView = new TableView<User>();
        tableView.getColumns().add(indexColumn);
        tableView.getColumns().add(userNameColumn);
        tableView.getColumns().add(roleColumn);
        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        Text dummyLabel = new Text("");
        VBox leftPane = new VBox(5);
        leftPane.getChildren().addAll(usersLabel, tableView);
        VBox rightPane = new VBox(20);
        rightPane.setFillWidth(true);
        rightPane.getChildren().addAll(dummyLabel, createUserButton);
        GridPane mainPane = new GridPane();
        mainPane.setPadding(new Insets(10, 0, 0, 10));
        mainPane.setHgap(20);
        mainPane.add(leftPane, 0, 0);
        mainPane.add(rightPane, 1, 0);
        float width = 372f;
        float height = 470f;
        Scene scene = new Scene(mainPane, width, height);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        primaryStage.setX((screenBounds.getWidth() - width) / 2);
        primaryStage.setY((screenBounds.getHeight() - height) / 2);
        showDialog();
    }

    private void showDialog() {
        dialog = new Dialog<>();
        dialog.setTitle("Login");
        dialog.setHeaderText("Please enter User Name and Password to login.");
        dialog.setResizable(false);
        Label userNameLabel = new Label("User Name:");
        Label passwordLabel = new Label("Password:");
        TextField userNameField = new TextField();
        PasswordField passwordField = new PasswordField();
        GridPane grid = new GridPane();
        grid.setAlignment(Pos.CENTER);
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(20, 35, 20, 35));
        grid.add(userNameLabel, 1, 1);
        grid.add(userNameField, 2, 1);
        grid.add(passwordLabel, 1, 2);
        grid.add(passwordField, 2, 2);
        dialog.getDialogPane().setContent(grid);
        dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
        Button okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
        okButton.addEventFilter(ActionEvent.ACTION, event -> {
            login(userNameField.getText().trim(), passwordField.getText());
            event.consume();
        });
        dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
        float width = 509f;
        float height = 168f;
        dialog.setWidth(width);
        dialog.setHeight(height);
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        dialog.setX((screenBounds.getWidth() - width) / 2);
        dialog.setY((screenBounds.getHeight() - height) / 2);
        dialog.showAndWait();
    }

    private void login(String userName, String password) {
        dialog.getDialogPane().setDisable(true);
        dialog.getDialogPane().getScene().setCursor(Cursor.WAIT);
        Task<Boolean> task = new Task<Boolean>() {
            @Override
            public Boolean call() {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException exception) {
                }
                return Boolean.TRUE;
            }
        };
        task.setOnSucceeded(e -> {
            Boolean success = task.getValue();
            dialog.getDialogPane().setDisable(false);
            dialog.getDialogPane().getScene().setCursor(Cursor.DEFAULT);
            if (success.booleanValue()) {
                Platform.runLater(() -> {
                    primaryStage.show();
                });
            } else {
                Alert alert = new Alert(AlertType.ERROR);
                alert.setTitle("Login Error");
                alert.setHeaderText("Unable to login.");
                alert.showAndWait();
            }
        });
        new Thread(task).start();
    }

    public static void main(String[] arguments) {
        Application.launch(arguments);
    }

}

class User {

    private StringProperty index;

    private StringProperty userName;

    private StringProperty role;

    public String getIndex() {
        return indexProperty().get();
    }

    public StringProperty indexProperty() {
        if (index == null) {
            index = new SimpleStringProperty(this, "index");
        }
        return index;
    }

    public void setIndex(String index) {
        indexProperty().set(index);
    }

    public String getUserName() {
        return userNameProperty().get();
    }

    public StringProperty userNameProperty() {
        if (userName == null) {
            userName = new SimpleStringProperty(this, "userName");
        }
        return userName;
    }

    public void setUserName(String userName) {
        userNameProperty().set(userName);
    }

    public String getRole() {
        return roleProperty().get();
    }

    public StringProperty roleProperty() {
        if (role == null) {
            role = new SimpleStringProperty(this, "role");
        }
        return role;
    }

    public void setRole(String role) {
        roleProperty().set(role);
    }

}

Solution de JKostikiadis:

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class TestApp extends Application {

    private static final double WIDTH = 316.0;
    private static final double HEIGHT = 339.0;

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

    @Override
    public void start(Stage stage) throws Exception {

        HBox pane = new HBox();
        pane.setAlignment(Pos.CENTER);

        Button b = new Button("click me");
        b.setOnAction(e -> {
            showDialog();
        });

        pane.getChildren().add(b);

        Scene scene = new Scene(pane, 300, 300);

        stage.setScene(scene);

        centerStage(stage, WIDTH, HEIGHT);
        stage.show();


    }

    private void showDialog() {
        Alert dialog = new Alert(AlertType.ERROR);
        dialog.setTitle("Error Dialog");
        dialog.setHeaderText("Look, an Error Dialog");
        dialog.setContentText("Ooops, there was an error!\nOoops, there was an error!");

        Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
        centerStage(stage, -10000, -10000);
        dialog.show();
        System.out.println(stage.getWidth() + " " + stage.getHeight());
        dialog.hide();
        centerStage(stage, stage.getWidth(), stage.getHeight());        
        dialog.showAndWait();

    }

    private void centerStage(Stage stage, double width, double height) {
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        stage.setX((screenBounds.getWidth() - width) / 2);
        stage.setY((screenBounds.getHeight() - height) / 2);
    }
}
Author: user3573403, 2017-10-06

3 answers

Eh bien parce que vous me demandez dans les félicitations, je vais fournir un exemple de réglage de la scène ( application principale ou boîte de dialogue ) au centre de l'écran par initialisation précoce de leurs dimensions.

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class TestApp extends Application {

    private static final double WIDTH = 316.0;
    private static final double HEIGHT = 339.0;

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

    @Override
    public void start(Stage stage) throws Exception {

        HBox pane = new HBox();
        pane.setAlignment(Pos.CENTER);

        Button b = new Button("click me");
        b.setOnAction(e -> {
            showDialog();
        });

        pane.getChildren().add(b);

        Scene scene = new Scene(pane, 300, 300);

        stage.setScene(scene);

        centerStage(stage, WIDTH, HEIGHT);
        stage.show();

        System.out.println(stage.getWidth() + " " + stage.getHeight());
    }

    private void showDialog() {
        Alert dialog = new Alert(AlertType.ERROR);
        dialog.setTitle("Error Dialog");
        dialog.setHeaderText("Look, an Error Dialog");
        dialog.setContentText("Ooops, there was an error!");

        Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
        centerStage(stage, 366, 175);

        dialog.showAndWait();

    }

    private void centerStage(Stage stage, double width, double height) {
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        stage.setX((screenBounds.getWidth() - width) / 2);
        stage.setY((screenBounds.getHeight() - height) / 2);
    }
}

Dans l'exemple ci-dessus, vous verrez que j'ai spécifié les dimensions de l'application à 300 300 mais j'utilise pour width = 316.0 et height = 339.0 et vous vous demandez peut-être pourquoi. C'est parce que la taille de la scène sera toujours un peu plus grande que la Scène (bordures + Barre de titre, etc ) afin de trouver la largeur et la hauteur de la Scène, vous aurez pour imprimer les dimensions de la scène après vous le montrer. La même logique se produit à la boîte de dialogue.

Important: Bien sûr, vous pouvez oublier tout ce qui précède et simplement faire:

stage.setWidth(300); // or a variable here 
stage.setHeight(300);

Mais cela affectera vos composants internes car si auparavant les composants de la scène avaient une taille de 300 300, ils vont maintenant être réduits à quelque chose de moins afin de rendre la scène fixe la taille de 300 300 donc dans ce cas oui, cela pourrait affecter l'apparence de votre application.

Dans le passé, je cherchais un moyen de trouver la dimension d'une étiquette avant de la montrer. J'ai découvert qu'il était possible d'obtenir ses dimensions en l'ajoutant à la scène, puis en appelant

labe.impl_processCSS(true);
System.out.println(labe.prefWidth(-1) + "/" + labe.prefHeight(-1));

Maintenant, si j'essaie de faire la même chose pour le volet principal dans l'application ci-dessus, il montre 59/25 quelles sont les dimensions du bouton lui-même, donc cette approche ne fonctionnera pas au cas où quelqu'un se poserait des questions à ce sujet.

Modifier :

Je ne veux pas vraiment montrer ce "hack" parce que je le trouve stupide et je suis sûr qu'il y a un meilleur moyen, mais jusqu'à ce que je le trouve ici, vous allez:

private void showDialog() {
    Alert dialog = new Alert(AlertType.ERROR);
    dialog.setTitle("Error Dialog");
    dialog.setHeaderText("Look, an Error Dialog");
    dialog.setContentText("Ooops, there was an error!\nOoops, there was an error!");

    Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
    centerStage(stage, -10000, -10000);
    dialog.show();
    centerStage(stage, stage.getWidth(), stage.getHeight());   
}
 0
Author: JKostikiadis, 2017-10-10 10:45:05

Malheureusement, vous devez attendre que la largeur / hauteur du Window (ou Dialog) soit calculée ainsi que le Window soit affiché. Puisque le Window est visible, vous remarquerez toujours que la fenêtre se déplace lors de la mise à jour de la position xy.

Faire la mise à jour lorsque l'événement WindowEvent.WINDOW_SHOWN est déclenché peut fournir un meilleur résultat:

   final Window window = dialog.getDialogPane().getScene().getWindow();

    window.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
        @Override
        public void handle(WindowEvent event) {
            Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
            window.setX((screenBounds.getWidth() - window.getWidth()) / 2);
            window.setY((screenBounds.getHeight() - window.getHeight()) / 2);

        }
    });

Et pour le primaryStage

    primaryStage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {

    @Override
    public void handle(WindowEvent event) {
        Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
        primaryStage.setX((screenBounds.getWidth() - primaryStage.getWidth()) / 2);
        primaryStage.setY((screenBounds.getHeight() - primaryStage.getHeight()) / 2);
    }
});
primaryStage.show();

Mais comme mentionné par JKostikiadis, une solution meilleure et appropriée pourrait être de calculer votre propre dimension avec le respect de la taille de l'écran.

Voici la petite amélioration que je peux voir. Lors de l'exécution de votre démo sur ma machine, le mouvement est erratique:

entrez la description de l'image ici

Je peux voir une petite amélioration lors de l'utilisation de WindowEvent.WINDOW_SHOWN (sans utiliserPlatform.runLater pour la première boîte de dialogue):

entrez la description de l'image ici

Quoi qu'il en soit, je ne pense pas que l'utilisation de Platform.runLater pour afficher la première fenêtre soit idéale car il n'y a aucune garantie que showAndWait() sera toujours exécuté avant le Runnable

 3
Author: shaka-b, 2017-10-08 10:53:14

Vous pouvez centrer une étape sur une autre étape avant de la rendre en appliquant le CSS qui vous fournira la largeur/hauteur.

Par exemple.

D'où vous créez la scène:

WindowHelper.centerChildWindowOnStage(stage, primaryStage);  //assuming primary is the stage you want to center on
stage.show();

Ci-dessous est le code pour centrer la fenêtre non shown (supposons que ce soit sur une classe WindowHelper à réutiliser dans l'application).

public static void centerChildWindowOnStage(Stage stage, Stage primaryStage ) {

    if(primaryStage == null){
        return;
    }

    double x = stage.getX();
    double y = stage.getY();

    // Firstly we need to force CSS and layout to happen, as the dialogPane
    // may not have been shown yet (so it has no dimensions)
    stage.getScene().getRoot().applyCss();
    stage.getScene().getRoot().layout();

    final Scene ownerScene = primaryStage.getScene();
    final double titleBarHeight = ownerScene.getY();

    // because Stage does not seem to centre itself over its owner, we
    // do it here.

    // then we can get the dimensions and position the dialog appropriately.
    final double dialogWidth = stage.getScene().getRoot().prefWidth(-1);
    final double dialogHeight = stage.getScene().getRoot().prefHeight(dialogWidth);

    final double ownerWidth = primaryStage.getScene().getRoot().prefWidth(-1);
    final double ownerHeight = primaryStage.getScene().getRoot().prefHeight(ownerWidth);

    if(dialogWidth < ownerWidth){
        x = primaryStage.getX() + (ownerScene.getWidth() / 2.0) - (dialogWidth / 2.0);
    }else {
        x = primaryStage.getX();
        stage.setWidth(dialogWidth);
    }

    if(dialogHeight < ownerHeight){
        y = primaryStage.getY() + titleBarHeight / 2.0 + (ownerScene.getHeight() / 2.0) - (dialogHeight / 2.0);
    }else {
        y = primaryStage.getY();
    }

    stage.setX(x);
    stage.setY(y);
}
 1
Author: purring pigeon, 2017-10-10 13:47:01