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