Java FX 使用 fxml 文件创建自定义对话框。如何设置或从中获取结果?
Java FX create Custom Dialog with fxml file. How to set or get result from it?
在这里,我想创建一个自定义对话框,其工作方式类似于 Alert
,具有漂亮的 UI; “showDialog.fxml”是 DialogPane
.
我试过 JFXDialog
但它没有等待,也没有给我用户的选择权。有什么办法可以搞定result
?提前致谢。
这是我的 Alert
实现:
primaryStage.setOnCloseRequest(event -> {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Выход");
alert.setHeaderText("Are you really want to exit ?");
ButtonType yes = new ButtonType("Yes");
ButtonType no = new ButtonType("No");
alert.getButtonTypes().clear();
alert.getButtonTypes().addAll(yes, no);
Optional<ButtonType> option = alert.showAndWait();
if (option.get() == yes) {
System.exit(0);
} else {
alert.close();
event.consume();
}
}
这是新的。
primaryStage.setOnCloseRequest(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/common/showDialog.fxml"));
ShowDialogController controller = loader.getController();
Dialog<JFXButton> dialog = new Dialog<>();
DialogPane confirmationDialogView = null;
try {
dialog.setDialogPane(loader.load());
} catch (IOException e) {
e.printStackTrace();
}
dialog.showAndWait();
}
这是 FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import de.jensd.fx.glyphs.octicons.OctIconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<DialogPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<AnchorPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css">
<GridPane layoutX="161.0" layoutY="84.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<OctIconView fx:id="octionIconView" fill="WHITE" glyphName="ALERT" size="200" wrappingWidth="198.0" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</GridPane>
<GridPane GridPane.columnIndex="1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" percentHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<JFXButton fx:id="btnNo" prefHeight="94.0" prefWidth="295.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="НЕТ" GridPane.halignment="LEFT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="50.0" />
</GridPane.margin>
</JFXButton>
<JFXButton fx:id="btnYes" prefHeight="94.0" prefWidth="337.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="ДА" GridPane.halignment="RIGHT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets right="50.0" />
</GridPane.margin>
</JFXButton>
<Label fx:id="labelInformation" alignment="CENTER" text="Label" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</GridPane>
</GridPane>
</AnchorPane>
</graphic>
</DialogPane>
这是一个 implementation of MessageBox,它扩展了 Alert
class。如果你想添加CSS样式来美化你的window,你必须在加载应用程序时定义MessageBox.setStyleSheet
(只需要一次,不需要每次都调用它来显示一个 MessageBox
):
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
MessageBox.setStyleSheet("/your/path/.../message-box.css");
...
primaryStage.setOnCloseRequest(event -> {
try {
MessageBox dialog = new MessageBox(AlertType.CONFIRMATION, primaryStage, "Do you really want to exit?", ButtonType.YES, ButtonType.NO);
dialog.setTitle("Выход");
Optional<ButtonType> option = dialog.showAndWait();
if (option != null && option.get() == ButtonType.YES)
System.exit(0);
else {
dialog.close();
event.consume();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
您可以检查message-box.css是否要自定义组件样式。
javaFX 中的对话框是 TEMPLATE class Dialog<R>. Where R is the return type (if missing, ButtonType 默认使用的实例。
对于您自己的对话框实现,您必须展开 Dialog 和 e 如果您想从中获取不同类型的数据,请提交 ResultConverter (instance of Callback<ButtonType, R>). Another option to get the result of a dialog is to manually call setResult and then close 对话框。
当你想使用FXML时,你通常会使用你的dialog的结构从对应的fxml[=中加载想要的DialogPane 28=] 文件.
这是一个例子:
<DialogPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefWidth="500">
<content>
<GridPane hgap="5" vgap="5">
<Label text="Адрес на хост:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<TextField fx:id="hostnameTextField" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<Label text="База данни:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextField fx:id="databaseTextField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label text="Потребителско име:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
<TextField fx:id="usernameTextField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.fillWidth="false"/>
<Label text="Парола:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
<PasswordField fx:id="passwordField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.fillWidth="false"/>
<columnConstraints>
<ColumnConstraints/>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
</GridPane>
</content>
<buttonTypes>
<ButtonType fx:id="connectButtonType" text="Свързване" buttonData="OK_DONE"/>
<ButtonType text="Затвори" buttonData="CANCEL_CLOSE"/>
</buttonTypes>
</DialogPane>
public class ConnectDialog extends Dialog<Connection> {
@FXML
private TextField hostnameTextField;
@FXML
private TextField databaseTextField;
@FXML
private TextField usernameTextField;
@FXML
private PasswordField passwordField;
@FXML
private ButtonType connectButtonType;
private ObjectProperty<Connection> connection = new SimpleObjectProperty<>(null);
public ConnectDialog(Window owner) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/com/xelapos/gui/client/dialog/dialog_pane_connect.fxml"));
loader.setController(this);
DialogPane dialogPane = loader.load();
dialogPane.lookupButton(connectButtonType).addEventFilter(ActionEvent.ANY, this::onConnect);
initOwner(owner);
initModality(Modality.APPLICATION_MODAL);
setResizable(true);
setTitle("Свързване с база данни");
setDialogPane(dialogPane);
setResultConverter(buttonType -> {
if(!Objects.equals(ButtonBar.ButtonData.OK_DONE, buttonType.getButtonData())) {
return null;
}
return connection.getValue();
});
setOnShowing(dialogEvent -> Platform.runLater(() -> hostnameTextField.requestFocus()));
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@FXML
private void initialize() {
}
@FXML
private void onConnect(ActionEvent event) {
String jdbcUrl = String.format(
"jdbc:postgresql://%s/%s",
hostnameTextField.textProperty().getValueSafe(),
databaseTextField.textProperty().getValueSafe()
);
try {
Connection conn = DriverManager.getConnection(jdbcUrl, usernameTextField.textProperty().getValueSafe(), passwordField.textProperty().getValueSafe());
connection.setValue(conn);
return;
}
catch (SQLException e) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.initOwner(getDialogPane().getScene().getWindow());
alert.initModality(Modality.APPLICATION_MODAL);
alert.setResizable(true);
alert.setTitle(getTitle());
alert.setHeaderText(null);
alert.setContentText(e.getLocalizedMessage());
alert.show();
}
event.consume();
}
}
当然还有用途:
ConnectDialog dialog = new ConnectDialog(stage);
dialog.showAndWait().ifPresent(conn -> {
System.out.println("Open connection!");
connection.setValue(conn);
});
在这里,我想创建一个自定义对话框,其工作方式类似于 Alert
,具有漂亮的 UI; “showDialog.fxml”是 DialogPane
.
我试过 JFXDialog
但它没有等待,也没有给我用户的选择权。有什么办法可以搞定result
?提前致谢。
这是我的 Alert
实现:
primaryStage.setOnCloseRequest(event -> {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Выход");
alert.setHeaderText("Are you really want to exit ?");
ButtonType yes = new ButtonType("Yes");
ButtonType no = new ButtonType("No");
alert.getButtonTypes().clear();
alert.getButtonTypes().addAll(yes, no);
Optional<ButtonType> option = alert.showAndWait();
if (option.get() == yes) {
System.exit(0);
} else {
alert.close();
event.consume();
}
}
这是新的。
primaryStage.setOnCloseRequest(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/common/showDialog.fxml"));
ShowDialogController controller = loader.getController();
Dialog<JFXButton> dialog = new Dialog<>();
DialogPane confirmationDialogView = null;
try {
dialog.setDialogPane(loader.load());
} catch (IOException e) {
e.printStackTrace();
}
dialog.showAndWait();
}
这是 FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import de.jensd.fx.glyphs.octicons.OctIconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<DialogPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<AnchorPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css">
<GridPane layoutX="161.0" layoutY="84.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<OctIconView fx:id="octionIconView" fill="WHITE" glyphName="ALERT" size="200" wrappingWidth="198.0" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
</GridPane>
<GridPane GridPane.columnIndex="1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" percentHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<JFXButton fx:id="btnNo" prefHeight="94.0" prefWidth="295.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="НЕТ" GridPane.halignment="LEFT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets left="50.0" />
</GridPane.margin>
</JFXButton>
<JFXButton fx:id="btnYes" prefHeight="94.0" prefWidth="337.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="ДА" GridPane.halignment="RIGHT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets right="50.0" />
</GridPane.margin>
</JFXButton>
<Label fx:id="labelInformation" alignment="CENTER" text="Label" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</GridPane>
</GridPane>
</AnchorPane>
</graphic>
</DialogPane>
这是一个 implementation of MessageBox,它扩展了 Alert
class。如果你想添加CSS样式来美化你的window,你必须在加载应用程序时定义MessageBox.setStyleSheet
(只需要一次,不需要每次都调用它来显示一个 MessageBox
):
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
MessageBox.setStyleSheet("/your/path/.../message-box.css");
...
primaryStage.setOnCloseRequest(event -> {
try {
MessageBox dialog = new MessageBox(AlertType.CONFIRMATION, primaryStage, "Do you really want to exit?", ButtonType.YES, ButtonType.NO);
dialog.setTitle("Выход");
Optional<ButtonType> option = dialog.showAndWait();
if (option != null && option.get() == ButtonType.YES)
System.exit(0);
else {
dialog.close();
event.consume();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
您可以检查message-box.css是否要自定义组件样式。
javaFX 中的对话框是 TEMPLATE class Dialog<R>. Where R is the return type (if missing, ButtonType 默认使用的实例。
对于您自己的对话框实现,您必须展开 Dialog 和 e 如果您想从中获取不同类型的数据,请提交 ResultConverter (instance of Callback<ButtonType, R>). Another option to get the result of a dialog is to manually call setResult and then close 对话框。
当你想使用FXML时,你通常会使用你的dialog的结构从对应的fxml[=中加载想要的DialogPane 28=] 文件.
这是一个例子:
<DialogPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefWidth="500">
<content>
<GridPane hgap="5" vgap="5">
<Label text="Адрес на хост:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<TextField fx:id="hostnameTextField" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<Label text="База данни:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<TextField fx:id="databaseTextField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label text="Потребителско име:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
<TextField fx:id="usernameTextField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.fillWidth="false"/>
<Label text="Парола:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
<PasswordField fx:id="passwordField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.fillWidth="false"/>
<columnConstraints>
<ColumnConstraints/>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
</GridPane>
</content>
<buttonTypes>
<ButtonType fx:id="connectButtonType" text="Свързване" buttonData="OK_DONE"/>
<ButtonType text="Затвори" buttonData="CANCEL_CLOSE"/>
</buttonTypes>
</DialogPane>
public class ConnectDialog extends Dialog<Connection> {
@FXML
private TextField hostnameTextField;
@FXML
private TextField databaseTextField;
@FXML
private TextField usernameTextField;
@FXML
private PasswordField passwordField;
@FXML
private ButtonType connectButtonType;
private ObjectProperty<Connection> connection = new SimpleObjectProperty<>(null);
public ConnectDialog(Window owner) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/com/xelapos/gui/client/dialog/dialog_pane_connect.fxml"));
loader.setController(this);
DialogPane dialogPane = loader.load();
dialogPane.lookupButton(connectButtonType).addEventFilter(ActionEvent.ANY, this::onConnect);
initOwner(owner);
initModality(Modality.APPLICATION_MODAL);
setResizable(true);
setTitle("Свързване с база данни");
setDialogPane(dialogPane);
setResultConverter(buttonType -> {
if(!Objects.equals(ButtonBar.ButtonData.OK_DONE, buttonType.getButtonData())) {
return null;
}
return connection.getValue();
});
setOnShowing(dialogEvent -> Platform.runLater(() -> hostnameTextField.requestFocus()));
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@FXML
private void initialize() {
}
@FXML
private void onConnect(ActionEvent event) {
String jdbcUrl = String.format(
"jdbc:postgresql://%s/%s",
hostnameTextField.textProperty().getValueSafe(),
databaseTextField.textProperty().getValueSafe()
);
try {
Connection conn = DriverManager.getConnection(jdbcUrl, usernameTextField.textProperty().getValueSafe(), passwordField.textProperty().getValueSafe());
connection.setValue(conn);
return;
}
catch (SQLException e) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.initOwner(getDialogPane().getScene().getWindow());
alert.initModality(Modality.APPLICATION_MODAL);
alert.setResizable(true);
alert.setTitle(getTitle());
alert.setHeaderText(null);
alert.setContentText(e.getLocalizedMessage());
alert.show();
}
event.consume();
}
}
当然还有用途:
ConnectDialog dialog = new ConnectDialog(stage);
dialog.showAndWait().ifPresent(conn -> {
System.out.println("Open connection!");
connection.setValue(conn);
});