阻止程序执行直到用户单击按钮

Sna*_*Doc 2 java user-interface javafx

我正在尝试制作类似于Swing's 的模态对话框JOptionPane.我想提出一个确认对话框,在我在代码中执行某些操作之前,该对话框会让用户明确地说"是".

我从以下网站无耻地窃取了这个例子:https://gist.github.com/jewelsea/1887631

并将其修改为:

public class ModalConfirmDialog {
    private boolean confirm = false;
    private boolean isDialogOpen = false;

    public boolean isOpen() {
        return isDialogOpen;
    }

    public boolean getConfirmation() {
        return confirm;
    }

    public void showConfirmationDialog(final Stage primaryStage) {
        // initialize the confirmation dialog
        final Stage dialog = new Stage(StageStyle.TRANSPARENT);
        dialog.initModality(Modality.WINDOW_MODAL);
        dialog.initOwner(primaryStage);
        dialog.setScene(
                new Scene(
                        HBoxBuilder.create().styleClass("modal-dialog").children(
                                LabelBuilder.create().text("Confirm Action?").build(),
                                ButtonBuilder.create().text("Yes").defaultButton(true).onAction(new EventHandler<ActionEvent>() {
                                    @Override public void handle(ActionEvent actionEvent) {
                                        // take action and close the dialog.
                                        primaryStage.getScene().getRoot().setEffect(null);
                                        dialog.close();
                                        confirm = true;
                                        isDialogOpen = false;
                                    }
                                }).build(),
                                ButtonBuilder.create().text("No").cancelButton(true).onAction(new EventHandler<ActionEvent>() {
                                    @Override public void handle(ActionEvent actionEvent) {
                                        // abort action and close the dialog.
                                        primaryStage.getScene().getRoot().setEffect(null);
                                        dialog.close();
                                        confirm = false;
                                        isDialogOpen = false;
                                    }
                                }).build()
                                ).build()
                                , Color.TRANSPARENT
                        )
                );
        dialog.getScene().getStylesheets().add(getClass().getResource("modal-dialog.css").toExternalForm());

        // allow the dialog to be dragged around.
        final Node root = dialog.getScene().getRoot();
        final Delta dragDelta = new Delta();
        root.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = dialog.getX() - mouseEvent.getScreenX();
                dragDelta.y = dialog.getY() - mouseEvent.getScreenY();
            }
        });
        root.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
                dialog.setX(mouseEvent.getScreenX() + dragDelta.x);
                dialog.setY(mouseEvent.getScreenY() + dragDelta.y);
            }
        });
        primaryStage.getScene().getRoot().setEffect(new BoxBlur());
        dialog.show();
        isDialogOpen = true;
    }

    //records relative x and y co-ordinates.
    class Delta { double x, y; }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,当弹出此对话框时,即使用户未选择选项(是或否),程序仍会继续执行.

我已经尝试设置一个布尔值并在循环中检查它以了解我应该检查用户选择的时间,但这最终导致其他程序(竞争条件和/或阻止包括对话框在内的所有内容).

    ModalConfirmDialog dialog = new ModalConfirmDialog();
    dialog.showConfirmationDialog((Stage) relatedTransTable.getScene().getWindow());
    while (dialog.isOpen()) {
        // wait for it to close?
    }

    if (dialog.getConfirmation()) {
        System.out.println("Confirmed choice!");
    } else {
        System.out.println("User denied choice!");
    }
Run Code Online (Sandbox Code Playgroud)

我不确定如何复制JOptionPane 而不需要嵌入Swing我的完整JavaFX应用程序.

jew*_*sea 7

解

使用showAndWait显示对话框.

显示此阶段并在返回调用者之前等待隐藏(关闭)它.此方法临时阻止当前事件的处理,并启动嵌套事件循环以处理其他事件.


我正在尝试创建一个类似于Swing的JOptionPane的模态对话框.

javafx.scene.control.AlertJava 8u40中的类提供了与Swing的JOptionPane类似的功能,其设计和实现受到了JOptionPane的严重影响.


目前的建议

  1. 对于警报或确认等基本常见对话框,请使用JavaFX 8u40 +工具包中的内置对话框功能. 8u40的早期访问版本可用,对话框API稳定,将映射到预计的2015年3月生产版本.
  2. 如果您需要特定类型的对话框,例如登录,字体选择,进度反馈和向导等任务,请使用ControlsFX(它支持比核心JavaFX平台更多的默认对话框实现).

  3. 如果您需要完全自定义的对话框,请使用本答案后面概述的技术.

背景

不要重新发明轮子.将ControlsFX库用于JOptionPane类型对话框,而不是编写自己的实现(而JavaFX 8u40将在平台中引入常用对话框).

您为模态对话框显示引用的代码现在已经很老了.在JavaFX核心平台和第三方库中实现了许多功能(包括添加showAndWait),这意味着如果你今天正在完成任务,它可能会以不同的方式完成.

问题中引用的代码使用了在Java 8中已弃用的Builders,因此不再建议使用它们(它们甚至不再出现在JavaFX javadoc中).

代码演示内置的JavaFX 8u40 + Alert类

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ShowAndWaitBuiltInAlert extends Application {
    @Override public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(event -> {
            Alert dialog = new Alert(
                    Alert.AlertType.CONFIRMATION,
                    "Are you sure you want to exit the Dialog Demo Application?"
            );
            dialog.showAndWait()
                    .filter(response -> response.equals(ButtonType.OK))
                    .ifPresent(response -> stage.close());
        });

        stage.setScene(new Scene(showDialog));
        stage.show();
    }

    public static void main(String[] args) { launch(args); }
}
Run Code Online (Sandbox Code Playgroud)

警报从Dialog扩展,因此可以使用Dialog类的所有自定义方法进行自定义.

代码演示内置的JavaFX 8u40 + Dialog类

确认对话框一样

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ShowAndWaitBuiltInDialog extends Application {    
    @Override public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(e -> {
            Dialog<ButtonType> dialog = new Dialog<>();
            dialog.setTitle("Dialog Demo");
            dialog.setHeaderText("Confirm Exit");
            dialog.setContentText("Are you sure you want to exit the Dialog Demo Application?");
            ButtonType exit = new ButtonType("Exit", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(
                    exit, ButtonType.CANCEL
            );
            dialog.showAndWait()
                    .filter(response -> response.equals(exit))
                    .ifPresent(response -> stage.close());
        });

        stage.setScene(new Scene(showDialog));
        stage.show();
    }

    public static void main(String[] args) { launch(args); }
}
Run Code Online (Sandbox Code Playgroud)

自定义对话框代码演示showAndWait

以下代码将在Java 8u40之前的Java 8版本中运行.

dialogsample

import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class ShowAndWaitDialog extends Application {

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

    @Override
    public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(e -> {
            ConfirmationDialog dialog = new ConfirmationDialog(
                    "Would you like to exit the application?"
            );
            dialog.setY(stage.getY() + stage.getHeight() + 10);
            dialog.setX(stage.getX());
            dialog.showAndWait();
            if (dialog.isSelected()) {
                stage.close();
            }
        });
        stage.setScene(new Scene(showDialog));
        stage.show();
    }

    class ConfirmationDialog extends Stage {
        private VBox layout = new VBox();

        private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
        public boolean isSelected() {
            return selected.get();
        }
        public ReadOnlyBooleanProperty selectedProperty() {
            return selected.getReadOnlyProperty();
        }

        public ConfirmationDialog(String question) {
            initStyle(StageStyle.UTILITY);
            initModality(Modality.APPLICATION_MODAL);

            layout.setSpacing(10);
            layout.setPadding(new Insets(10));

            createControls();

            layout.getChildren().addAll(
                    new Label(question),
                    createControls()
            );

            setScene(new Scene(layout));
            sizeToScene();  // workaround because utility stages aren't automatically sized correctly to their scene.
        }

        private HBox createControls() {
            final Button ok = new Button("OK");
            ok.setOnAction(e -> {
                selected.set(true);
                close();
            });

            final Button cancel = new Button("Cancel");
            cancel.setOnAction(e -> {
                selected.set(false);
                close();
            });

            final HBox controls = new HBox(10, ok, cancel);
            controls.setAlignment(Pos.CENTER_RIGHT);

            return controls;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

2014年12月19日评论问题更新

这不是每个UI框架中预期的一些基本UI功能吗?为什么到目前为止JavaFX缺少这种原生功能?

showAndWait一直在JavaFX中的大约两年半,最初的JavaFX 2发布后,加入约8个月,直到showAndWait用JavaFX 2.2表现为公共API.在此期间,大多数showAndWait功能可以使用类似于提问者问题中的代码的事件处理程序进行模拟.

在更常见的内置通用对话框API中,例如警报和确认(现已添加到Java 8u40),JavaFX问题跟踪器为RT-12643功能提供了广泛的历史,设计讨论和初始延迟原因..