以下代码演示了对话框的居中和屏幕中央的舞台。应首先显示该对话框,供用户输入登录凭据。成功登录后,将显示主窗口(阶段)。我找到了从这个网站集中对话和舞台的解决方案,但它似乎并不理想。对于对话框和舞台,在我们计算坐标然后将它们放在中心之前,必须先显示它们。这意味着我们可以看到对话框和主窗口在显示后移动到中心。有没有更好的办法?理想情况下,它们应该在显示之前定位在中心。
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);
}
}
以下是将自定义尺寸设置为舞台和对话框的解决方案。它适用于舞台,但它不适用于对话框。
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);
}
}
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);
}
}
答案 0 :(得分:3)
不幸的是,您必须等待计算Window
(或Dialog
)的宽度/高度以及Window
的宽度/高度。由于Window
可见,因此在更新xy位置时,您将始终注意到窗口正在移动。
触发WindowEvent.WINDOW_SHOWN
事件时执行更新可能会提供更好的结果:
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);
}
});
对于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();
但正如JKostikiadis所提到的,更好和更合适的解决方案可能是根据当前屏幕尺寸计算自己的尺寸。
这是我能看到的小改进。 在我的机器上运行演示时,移动是不稳定的:
使用WindowEvent.WINDOW_SHOWN
(第一个对话框不使用Platform.runLater
)时,我可以看到一个小小的改进:
无论如何,我不认为使用Platform.runLater
来显示第一个窗口是理想的,因为无法保证在showAndWait()
之前始终执行Runnable
答案 1 :(得分:1)
您可以在渲染它之前将舞台居中在另一个舞台上,方法是应用将为您提供宽度/高度的css。
例如。
从你创建舞台的地方开始:
WindowHelper.centerChildWindowOnStage(stage, primaryStage); //assuming primary is the stage you want to center on
stage.show();
下面是未示出的窗口居中的代码(假设这是在一个WindowHelper类上,可以在应用程序中重用)。
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);
}
答案 2 :(得分:0)
好吧,因为你在评价中问我,我将提供一个例子,通过早期初始化它们的尺寸,将舞台(主应用程序或对话框)设置到屏幕中心。
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);
}
}
在上面的示例中,您将看到我已将应用程序维度指定为300,300,但我使用的是width = 316.0和height = 339.0,您可能想知道为什么。这是因为舞台大小总是比场景(边框+标题栏等)大一些,所以为了找到舞台的真实宽度和高度,你必须在你之后打印舞台的尺寸展示下。对话框也出现了同样的逻辑。
重要提示:当然,您可以忘记以上所有内容,并且只是这样做:
stage.setWidth(300); // or a variable here
stage.setHeight(300);
但这会影响你的内部组件,因为如果之前场景的组件大小为300,300,那么现在它们将被挤压到更少的东西,以便使舞台固定300,300的大小,以便case yes它可能会影响你的应用程序的样子。
在过去,我一直在寻找一种方法来查找标签的尺寸。我发现可以通过将其添加到场景中然后调用
来获得它的尺寸labe.impl_processCSS(true);
System.out.println(labe.prefWidth(-1) + "/" + labe.prefHeight(-1));
现在如果我尝试对上面的应用程序中的主窗格执行相同的操作,它会显示59/25这是按钮本身的尺寸,所以这种方法在某人想知道它的情况下不会起作用。
修改:
我真的不想展示这个&#34; hack&#34;因为我发现它很愚蠢,我确信有更好的方法,但直到我在这里找到你:
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());
}