【问题标题】:JavaFX: Disable all components while a process is running and show progress indicatorJavaFX:在进程运行时禁用所有组件并显示进度指示器
【发布时间】:2017-06-16 18:56:05
【问题描述】:

我有一个从数据库中读取值并返回Map<Integer,String>.的方法,此方法需要一些时间才能返回地图。

在读取时间值之前,我希望在屏幕上显示一个进度指示器(仅加载环形指示器就足够了,不需要进度条),并且所有其他组件都应禁用,直到显示时间进度条.

public void scanDevice() {
    ObservableList<TextField> list = FXCollections.observableArrayList(vehicleId, vehicleName, deviceType,
            offboardBroker1, offboardBroker2, postfixQueue, pKIServer);
    editedValuesMap.clear();
    // devicePlugged = true;
    if (cbChooseProject.getSelectionModel().getSelectedItem() != null) {
        try {
            devicePlugged = dsAdapter.getAdapter();
            if (devicePlugged) {
                if (bScanDevice.isFocused()) {
                    readMap = new HashMap<Integer, String>();
                    //Process Start
                    readMap = dsAdapter.initScan();
                    //Process End

                    if (!readMap.isEmpty() && readMap != null) {
                        isWritten = true;
                        isDeviceSideEnabled();
                        editDeviceContents.setDisable(false);
                        vehicleId.setText(readMap.get(0));
                        vehicleName.setText(readMap.get(1));
                        deviceType.setText(readMap.get(2));
                        offboardBroker1.setText(readMap.get(3));
                        offboardBroker2.setText(readMap.get(4));
                        postfixQueue.setText(readMap.get(5));
                        pKIServer.setText(readMap.get(6));
                        lContentsSerialNo.setText(readMap.get(7));
                    }
                }
            }

【问题讨论】:

    标签: java javafx progress progress-indicator


    【解决方案1】:

    这是SSCCE

    它使用可以多次启动的Service。它不完整,但可以开始。

    public class Main extends Application {
        @Override
        public void start(Stage primaryStage) {
    
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root, 400, 400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
    
            Service<Void> serv = new Service<Void>() {
    
                @Override
                protected Task<Void> createTask() {
                    return new Task<Void>() {
    
                        @Override
                        protected Void call() throws Exception {
                            int maxWork = 10;
                            for (int i = 0; i < maxWork; i++) {
                                Thread.sleep(1000);
                                updateProgress(i + 1, maxWork);
                            }
    
                            return null;
                        }
    
                        @Override
                        protected void succeeded() {
                            super.succeeded();
                            updateProgress(1, 1);
                        }
    
                        @Override
                        protected void cancelled() {
                            super.cancelled();
                            updateProgress(1, 1);
                        }
    
                        @Override
                        protected void failed() {
                            super.failed();
                            updateProgress(1, 1);
                        }
    
                    };
                }
            };
    
            ProgressIndicator pi = new ProgressIndicator();
            pi.progressProperty().bind(serv.progressProperty());
            
    
            Button bStart = new Button("Start");
            bStart.setOnAction(e -> {
                serv.reset();
                serv.start();
            });
    
            root.setCenter(bStart);
            root.setBottom(pi);
            
            primaryStage.setScene(scene);
            primaryStage.show();
            
            pi.getScene().getRoot().disableProperty().bind(serv.runningProperty());
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    在我添加的 CSS 中:

    .progress-indicator:disabled {
        -fx-opacity: 1;
    }
    

    【讨论】:

      【解决方案2】:

      您可以使用以下方法禁用所有节点,但如果您还想在某些事情发生时等待,则使用 StackPanes 的覆盖可能是首选。

      public void setNodesDiabled(boolean disable, Node... nodes) {
          for(Node node : nodes) {
              node.setDisable(disable);
          }
      }
      

      使用任意节点数,您可以禁用和重新启用与进程相关的尽可能多的节点。它还有助于清理,因为您不会有多个 node.setDisable(true); node2.setDisable(true); 等等。

      在此示例中,您不需要 setNodesDisabled(),因为 StackPane 叠加层可防止单击其中内容以外的任何内容。背景颜色是带有 70% alpha 的灰色,因此您可以判断它是一个叠加层。

      public class ProgressExample extends Application {
      
          public StackPane layout, main, progress;
      
          public StackPane createProgressPane() {
              ProgressIndicator indicator = new ProgressIndicator();
              indicator.setMaxHeight(50);
              indicator.setMaxWidth(50);
      
              StackPane pane = new StackPane();
              pane.setAlignment(Pos.CENTER);
              pane.setStyle("-fx-background-color: rgba(160,160,160,0.7)");
              pane.getChildren().add(indicator);
      
              Task<Void> task = new Task<Void>(){
                  protected Void call() throws Exception {
                      // Your process here.
                      // Any changes to UI components must be inside Platform.runLater() or else it will hang.
                      Thread.sleep(2000);
      
                      Platform.runLater(() -> {
                          layout.getChildren().remove(pane);
                      });
                      return null;
                  }
              };
              new Thread(task).start();
              return pane;
          }
      
          public StackPane createMainPane() {
              Label label = new Label("Hello World!");
              label.setFont(Font.font("Tahoma", FontWeight.SEMI_BOLD, 16));
      
              Button start = new Button("Start Process");
              start.setOnAction(action -> {
                  progress = createProgressPane();
                  layout.getChildren().add(progress);
              });
      
              VBox vbox = new VBox(10);
              vbox.setAlignment(Pos.CENTER);
              vbox.getChildren().addAll(label, start);
              vbox.setPadding(new Insets(10,10,10,10));
      
              StackPane pane = new StackPane();
              pane.getChildren().add(vbox);
              return pane;
          }
      
          public void start(Stage stage) throws Exception {
              main = createMainPane();
      
              layout = new StackPane();
              layout.getChildren().add(main);
      
              Scene scene = new Scene(layout, 900, 550);
              stage.setScene(scene);
              stage.setTitle("Progress Example");
              stage.setOnCloseRequest(e -> {
                  Platform.exit();
                  System.exit(0);
              });
              stage.show();
          }
      
          public static void main(String[] args) {
              launch(args);
          }
      }
      

      我认为问题在于您试图更改 Task 内的 TextFields 的值,这不是 FX 应用程序线程,这就是您获得 Not on FX application thread 的原因。要解决此问题,您需要将修改节点的行放在 Platform.runLater() 中,如下所示。

      if (readMap != null && !readMap.isEmpty()) { // Swap the order, can't check empty if it's null.
          isWritten = true;
          isDeviceSideEnabled();
          Platform.runLater(() -> {
              editDeviceContents.setDisable(false);
              vehicleId.setText(readMap.get(0));
              vehicleName.setText(readMap.get(1));
              deviceType.setText(readMap.get(2));
              offboardBroker1.setText(readMap.get(3));
              offboardBroker2.setText(readMap.get(4));
              postfixQueue.setText(readMap.get(5));
              pKIServer.setText(readMap.get(6));
              lContentsSerialNo.setText(readMap.get(7));
          });
      }
      

      【讨论】:

      • 你能告诉我这个 Thread.sleep(2000) 有什么用吗?我是否需要计算我的方法所需的时间,然后在这段时间内休眠线程,并且由于项目包含,我必须使用 BorderPane 布局。我收到以下错误:线程“Thread-5”中的异常 java.lang.IllegalStateException:不在 FX 应用程序线程上; currentThread = Thread-5
      • 示例中使用Thread.sleep(2000); 仅仅是因为它是一个示例。如果没有任何东西来花时间,进度窗格在测试时将不会显示足够长的时间。你不需要它。至于错误,您能否编辑问题以将这些行包含在Task 中?您需要将更改 FX 节点和窗格的行放在它们自己的 Platform.runLater() 中。
      • Mathews,我添加了包含我的流程的代码。该进程从设备中读取值并将其显示在 UI 上。
      猜你喜欢
      • 2016-06-08
      • 2017-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-12
      • 2011-09-29
      • 1970-01-01
      • 2013-11-22
      相关资源
      最近更新 更多