JavaFX:如何临时阻止GUI界面

5

我正在尝试确定是否可以阻止GUI。基本上,我的应用程序(使用 NetBeans 平台JavaFX)与服务器连接。

无论用户看到哪个屏幕,如果应用程序失去与服务器的连接,我希望阻止所有操作(用户无法打开任何新窗口或单击任何地方),直到应用程序重新连接为止(无论需要5分钟还是5个小时)。然而,始终会在所有内容的顶部出现警报消息。

监听服务器连接的Java类没有任何对JavaFX容器的引用。这就是我实际拥有的东西:

 public class StatusConnectionObserver implements ConnectionObserver {

        private final Led led;

        private final Label label;

        public StatusConnectionObserver(Led led, Label label) {
            this.led = led;
            this.label = label;
        }

        @Override
        public void setConnected(boolean connected) {
            if (connected) {
                Platform.runLater(() -> {
                    led.setLedColor(Color.rgb(59, 249, 53));
                    label.setText("Connected");
                });

            } else {
                Platform.runLater(() -> {
                    led.setLedColor(Color.RED);
                    label.setText("Disconnected");  
                });                      
            }
        }
}

并且:

public class ConnectionComponent {

private Led led;

private Label label;

private HBox container;

private VBox ledContainer;

public ConnectionComponent() {
    initGraphics();
}

public Parent getView() {
    return this.container;
}

public void initGraphics() {
    //Here I set up the elements (label and Led) inside the container
}

这里称为:
    @ServiceProvider(service = StatusLineElementProvider.class)
public class ConnectionIndicator implements StatusLineElementProvider {

    @Override
    public Component getStatusLineElement() {
        JFXPanel fxPanel = new JFXPanel();
        Platform.setImplicitExit(false);
        new JavaFXUIThread().runOnUiToolkitThread(() -> {
            Scene scene = new Scene(new ConnectionComponent().getView());
            scene.getStylesheets().add(FXTheme.getDefault().getStylesheet());
            fxPanel.setScene(scene);
        });

        return fxPanel;
    }
}

该想法是在顶部显示某些内容(即使只是简单的文本消息),同时将后台应用程序设置为更加不透明。


你能澄清一下你发布的代码和你的UI代码之间的关系吗?根据你在答案中发布的评论,label不是JavaFX UI的一部分(所以我想知道为什么你要将这些调用包装在Platform.runLater(...)中)。这段代码属于哪个类?如果它没有访问JavaFX UI的权限,那么JavaFX UI是否有访问它的权限?肯定有某种联系。 - James_D
@James_D 我更新了我的代码。 - db92
抱歉,我想我还没有理解问题。这里有一个观察者模式:可能在某个地方你实例化了StatusConnectionObserver并注册它,以便它观察某种Connection对象。为什么不创建另一个具有访问UI权限的ConnectionObserver实现,以便它可以执行其他答案中描述的所有操作,并向相同的连接对象注册一个该实例?观察者模式的整个重点在于多个实体可以观察相同的数据。 - James_D
1
此外,这看起来更像是一个嵌入了JavaFX的Swing或AWT应用程序,而不是一个真正的JavaFX应用程序?因此,您可能应该使用Swing技术来显示模态对话框并禁用UI。 - James_D
@James_D 是的,我实例化了 'StatusConnectionObserver' 并直接在 'ConnectionComponent' 中注册它。实际上,该应用程序使用 NetBeans 平台运行,该平台由多个模块组成(就像 NetBeans IDE 一样)。JavaFX 仅用于不同组件内部的 GUI。 - db92
@James_D 你是对的!我发现NetBeans平台是基于Swing的。因此,即使我的应用程序的不同模块是使用JavaFX开发的,我仍然需要使用SWING来禁用整个UI界面。感谢你的提示。 - db92
2个回答

1
你需要一个模态对话框。创建这样的对话框,并在连接断开时显示它。然后使用一个线程定期检查连接是否恢复。当连接恢复时,关闭对话框。由于该对话框是模态的,意味着在它解决之前你无法对用户界面进行任何操作。参见this

这个解决方案与其他用户已经给出的解决方案非常相似。即使您使用警报,也无法在UI上执行任何操作,关键是如何将其保持在所有内容的顶部。关于线程,当连接状态改变时,setConnected()方法会自动调用,因此我不需要其他任何东西来检查连接状态。 - db92
但将其设置为模态意味着它在所有内容的顶部。还要将其设置为setAlwaysOnTop(true)。 - gvlachakis

0

使用 AlertDialog 组件。您可以通过 CSS 样式化它们或添加自定义内容。尝试这个最简单的解决方案:

Alert a = new Alert(Alert.AlertType.ERROR, "Connection error");

public void createAlert() {
    a.getDialogPane().getButtonTypes().clear();
    a.initModality(Modality.APPLICATION_MODAL);
    //*************** EDIT ***************
    a.initStyle(StageStyle.UNDECORATED);
    a.initOwner(label.getScene().getWindow());
    //************************************
}

@Override
public void setConnected(boolean connected) {
    if (connected) {
        Platform.runLater(() -> {
            label.setText("Connected");
            a.show();
        });

    } else {
        Platform.runLater(() -> {
            label.setText("Disconnected");
            a.close();
        });
    }
}

您还可以在整个场景(Scene)的顶部添加其他面板(Pane)

StackPane root = new StackPane();
root.getChildren().addAll(applicationContent);
Pane p = new Pane();
p.setStyle("-fx-background-color: rgba(31,31,31,0.6);");
//add Pane to root when disconnected
//root.getChildren().add(p);

Scene scene = new Scene(root, 300, 250);

就像我说的那样,我没有任何关于容器或其他方面的参考,所以我无法使用a.initOwner()。这个解决方案本身也可以工作,但有一个简单的问题:后台应用程序并没有完全禁用(我不能点击任何东西,这很好,但如果我尝试点击它,警报会自动最小化,而不是始终保持在顶部)。你有什么建议吗? - db92
你可以使用 a.initOwner(),因为你已经有了对 label 的引用。要设置所有者,请获取 label 组件的 Window。要禁用隐藏,您可以将对话框的样式设置为 UNDECORATED。请参见我的编辑。 - MBec
标签位于状态栏内(这是NetBeans平台的一个组件)。我无法从那里访问我的应用程序的主舞台。(我只能访问包含标签的HBox)。 - db92
每个“Node”都可以访问其“Scene”,因此如果您可以访问“HBox”,则调用“hbox.getScene().getWindow()”。 - MBec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接