【问题标题】:Making JavaFX Alerts/Dialogs Modal within Swing Application在 Swing 应用程序中制作 JavaFX 警报/对话框模式
【发布时间】:2015-03-19 10:28:52
【问题描述】:

因此,我们再次将完全使用 Swing 的现有 Java 应用程序转换为使用 JavaFX。但是,该应用程序不会完全使用 JavaFX。这似乎导致警报/对话框和模式出现一些问题。我们目前使用的是 Java 8u40。

主要的应用程序基本上是在一个有菜单的 JFrame 中。主要内容窗格是 JDesktopPane,单击 MenuItem 会在 DeskopPane 中打开新的 JInternalFrames。目前,我们正在转换为 JavaFX 的屏幕基本上是 JInternalFrame 中的 JFXPanel。从 JFXPanel 打开的任何警报/对话框都是模态面板本身,但不是 JInternalFrame、DeskopPane、菜单等。

我在 DialogPane 文档中读到,他们计划在 JavaFX 的未来版本中引入一些轻量级对话框,甚至可能是 InternalFrames,所以也许我们只需要再等一会儿才能获得这个功能。但是,理想情况下,当打开一个新的警报/对话框时,它将是整个应用程序的模态。

编辑: 目前对模态对话框执行以下操作:

((Stage)getDialogPane().getScene().getWindow()).setAlwaysOnTop(true);

这使对话框始终显示在顶部,但是即使我们的主应用程序已最小化,该对话框也仍位于其他应用程序的顶部。它也不会阻止框架中任何 Swing 组件的输入。

【问题讨论】:

  • 你的问题到底是什么?
  • 如何使 JavaFX 对话框成为整个 Swing 应用程序的模式?
  • 您打算如何将带有内部框架的 JDesktopPane 移植到 FX?那里似乎没有对应物。也许您会使用“对接框架”?根据 Jira 的说法,这仍未解决 (javafx-jira.kenai.com/browse/RT-14039)。你有什么代码我们可以专门讨论吗?
  • 我猜这是问题的一部分,以及为什么应用程序不会完全是 JavaFX 并且最终仍将嵌入到 Swing 应用程序中。
  • 我根本不了解您的应用程序,但也许您可以考虑一个新的使用概念 - 可能是手风琴、标签、拆分窗格或其他任何东西。我只是不相信你的需求只能用 Swing 而不是 FX。

标签: swing javafx modal-dialog alert jdesktoppane


【解决方案1】:

您可以使用以下解决方法,在显示Alert 时创建一个不可见的JDialog,并在关闭Alert 时处理JDialog。这种方法将模态扩展到整个应用程序,包括 Swing 部分。

// create Alert
Alert alert = new Alert(AlertType.INFORMATION, "Hello");

// create invisible JDialog and "show" it
JDialog dialog = new JDialog();
dialog.setModal(true);
dialog.setUndecorated(true);
dialog.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
SwingUtilities.invokeLater(() -> dialog.setVisible(true));

// show Alert
alert.showAndWait();

// close JDialog after Alert is closed
dialog.dispose();

【讨论】:

    【解决方案2】:

    我不认为我完全理解你的问题。但这是我的猜测 - 您正在尝试从一些 JFXPanel 生成一个警报窗口,该窗口将是模态的(即用户在关闭该警报窗口之前将无法单击您的应用程序)到您的整个应用程序,该应用程序是部分编写的使用摆动组件。

    如果您的应用程序将使用纯 JavaFX 编写,那么您将执行类似的操作(假设您在 JFXPanel 的某处创建了一个按钮)

    button.setOnAction(evt -> {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.initModality(Modality.APPLICATION_MODAL);
    
        // This will not work in your code
        alert.initOwner(button.getScene().getWindow());
    
        alert.show();
    });
    

    但由于initOwner 需要javafx.stage.window 对象,因此传递swing 组件将无法在您的代码中工作。从 Java 8u40 开始,我认为没有正确的方法(即不是 hacks)将 Alert 对象的所有权设置为 swing 组件。毫不奇怪,已经向here 提出了这样的问题,并且在撰写本文时没有回答。

    根据您的要求,您可以使用JOptionPane.showMessageDialog 方法,它看起来很像解决方法。

    button.setOnAction(evt -> {
        JOptionPane.showMessageDialog(desktopPane,"My message");
    });
    

    这些对话框默认是模态的,所以不需要做任何工作。您可以从 JavaFX 组件的任何事件处理程序方法中调用它们。

    【讨论】:

    • 感谢您的回复,这正是我所要求的。 JOptionPanes 的唯一问题是它们的外观和感觉与其他(大部分)JavaFX 屏幕不同。我可能需要修改现有代码,以便将 JDialog 与 JFXPanel 一起使用,正如上面 James_D 所建议的那样。
    • @Tommo 恐怕目前这是唯一的方法。但是您仍然可以将摇摆组件的外观设置为有点接近 JavaFX 外观。如果 Oracle 为 swing 提供 JavaFX 外观,但 Oracle 和某些第三方 L&F 均不可用,那就太好了。
    【解决方案3】:

    我已经用我的 JavaFXFrame 中实现的一个小接口做了一些解决方法:

    public interface DialogParent {
        void setOnFocusGained(EventHandler<FocusEvent> focusHandler);
        void setOnCloseRequest(EventHandler<WindowEvent> closeHandler);
    }
    

    还有我的 JavaFXFrame 实现

    public class JavaFXFrame implements DialogParent {
        private JFrame frame;
        private EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler;
        private EventHandler<javafx.stage.WindowEvent> windowClosingHandler;
    
    public void JavaFXFrame() {
        final JFXPanel fxPanel = new JFXPanel();
        frame = new JFrame();
        frame.add(fxPanel);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                tryClosing(this);
            }
        });
        frame.addWindowFocusListener(new WindowAdapter() {
            @Override
            public void windowGainedFocus(WindowEvent e) {
                if (focusGainedHandler != null) {
                    focusGainedHandler.handle(new FocusEvent());
                }
            }
        });
    }
    
    public void setVisible(boolean visible) {
        frame.setVisible(visible);
    }
    
    private void tryClosing(WindowListener listener) {
        javafx.stage.WindowEvent windowEvent = new javafx.stage.WindowEvent(null,    javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST);
        if (windowClosingHandler != null) {
            windowClosingHandler.handle(windowEvent);
        }
        if (!windowEvent.isConsumed()) {
            frame.setVisible(false);
        }
    }
    
    @Override
    public void setOnFocusGained(EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler) {
        this.focusGainedHandler = focusGainedHandler;
    }
    
    @Override
    public void setOnCloseRequest(EventHandler<javafx.stage.WindowEvent> windowClosingHandler) {
        this.windowClosingHandler = windowClosingHandler;
    }
    

    }

    并显示警报:

    public static void showAlert(Alert alert) {
        DialogPane dialogPane = alert.getDialogPane();
        final Stage stage = new Stage();
        stage.setScene(dialogPane.getScene());
        List<ButtonType> buttonTypes = dialogPane.getButtonTypes();
        for (ButtonType buttonType : buttonTypes) {
            ButtonBase button = (ButtonBase) dialogPane.lookupButton(buttonType);
            button.setOnAction(evt -> {
                dialogPane.setUserData(buttonType);
                stage.close();
            });
        }
        dialogParent.setOnFocusGained(event -> {
            stage.toFront();
        });
        dialogParent.setOnCloseRequest(Event::consume);
        stage.setOnCloseRequest(event -> {
            dialogParent.setOnFocusGained(null);
            dialogParent.setOnCloseRequest(null);
        });
        stage.show();
    }
    

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      • 2017-06-29
      • 2017-04-23
      • 1970-01-01
      • 2015-03-08
      • 1970-01-01
      相关资源
      最近更新 更多