Java FX создает пользовательский диалог с файлом fxml. Как установить или получить от него результат?

#javafx

#javafx

Вопрос:

Здесь я хочу создать пользовательский диалог, который работает как Alert , с красивым пользовательским интерфейсом; «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>
 

Ответ №1:

Диалоги в JavaFX являются экземпляром диалогового окна класса ШАБЛОНА<R>. Где R — возвращаемый тип (если отсутствует, по умолчанию используется buttonType).

Для вашей собственной реализации диалога вы должны развернуть Dialog и e если вы хотите получить из него данные другого типа, отправьте ResultConverter (экземпляр обратного вызова<buttonType, R> ). Другой вариант получения результата диалога — вручную вызвать SetResult и затем закрыть диалоговое окно.

Когда вы хотите использовать FXML, вы обычно используете структуру своего диалогового окна для загрузки нужной диалоговой панели из соответствующего файла fxml.

Вот пример:

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

Комментарии:

1. Я не знал, что есть конвертер результатов для диалогов, спасибо за это 🙂

Ответ №2:

Вот реализация MessageBox, которая расширяет Alert класс. Если вы хотите добавить стиль CSS для украшения вашего окна, вы должны определить 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 amp;amp; option.get() == ButtonType.YES)
                    System.exit(0);
                else {
                    dialog.close();
                    event.consume();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 

Вы можете проверить message-box.css, хотите ли вы настроить стиль компонента.