JavaFX: отключить все компоненты во время выполнения процесса и показать индикатор выполнения.

У меня есть метод, который считывает значения из базы данных и возвращает 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));
                    }
                }
            }

person Anurag Sharma    schedule 31.01.2017    source источник


Ответы (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%, так что вы можете сказать, что это наложение.

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

Я считаю, что проблема в том, что вы пытаетесь изменить значения TextFields внутри Task, который не является потоком приложения FX, поэтому вы получаете Not on FX application thread. Чтобы исправить это, вам нужно поместить свои строки, которые изменяют узлы, внутри Platform.runLater(), как показано ниже, в вашем операторе if.

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));
    });
}
person Matthew Wright    schedule 31.01.2017
comment
Не могли бы вы сказать мне, в чем польза этого Thread.sleep(2000); Нужно ли мне рассчитать время, требуемое моим методом, а затем заснуть поток на это время, и из-за содержания проекта я должен использовать макет BorderPane. Я получаю следующую ошибку: Исключение в потоке Thread-5 java.lang.IllegalStateException: не в потоке приложения FX; текущая нить = нить-5 - person Anurag Sharma; 01.02.2017
comment
Использование Thread.sleep(2000); в примере было просто потому, что это пример. Без чего-либо внутри, что требовало бы времени, панель прогресса не была бы видна достаточно долго при тестировании. Вам это не нужно. Что касается ошибки, не могли бы вы отредактировать вопрос, включив эти строки в Task? Вам нужно поместить свои линии, которые изменяют узлы и панели FX, в свои собственные Platform.runLater(). - person Matthew Wright; 01.02.2017
comment
Мэтьюз, я добавил код, содержащий мой процесс. Процесс считывает значения с устройства и отображает их в пользовательском интерфейсе. - person Anurag Sharma; 02.02.2017

Вот SSCCE:

Он использует Службу, которую можно запускать более однажды. Это не полное, но что-то, с чего можно начать.

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;
}
person DVarga    schedule 31.01.2017