web-dev-qa-db-fra.com

Exécuter la tâche en arrière-plan dans JavaFX

Je veux charger jusqu'à neuf panneaux dans un TilePane. Pour chaque volet, je dois lancer un calcul du contenu (environ 300 ms) et ensuite construire le panneau (environ 500 ms). Ce que je veux, c'est qu'il y ait neuf ProgressIndicators qui échangent avec chaque panneau après son calcul. Je l'ai essayé avec la commande Platform.runlater ainsi qu'avec une classe de service. Le résultat était toujours le même. Les ProgressIndicator sont représentés, mais pas animé. Après quelques secondes là-bas tous les panneaux à la fois. Existe-t-il une possibilité, que les indicateurs soient animés pendant la pause et que je puisse les échanger les uns après les autres?

10
Yggdrasil

JavaFX dispose d’un thread d’affectation d’événements qu’il utilise pour les événements d’interface utilisateur. Tous les travaux avec l'interface utilisateur devraient se produire sur ce fil. Et les calculs non liés à l'assurance-chômage ne devraient pas s'y produire pour éviter les retards dans l'interface utilisateur.

Voir le code suivant:

public class Indicators extends Application {

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

    @Override
    public void start(Stage stage) {
        Pane root = new HBox();
        stage.setScene(new Scene(root, 300, 100));

        for (int i = 0; i < 10; i++) {
            final ProgressIndicator pi = new ProgressIndicator(0);
            root.getChildren().add(pi);

            // separate non-FX thread
            new Thread() {

                // runnable for that thread
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        try {
                            // imitating work
                            Thread.sleep(new Random().nextInt(1000));
                        } catch (InterruptedException ex) {
                            ex.printStackTrace();
                        }
                        final double progress = i*0.05;
                        // update ProgressIndicator on FX thread
                        Platform.runLater(new Runnable() {

                            public void run() {
                                pi.setProgress(progress);
                            }
                        });
                    }
                }
            }.start();
        }

        stage.show();

    }
}
12
Sergey Grinev

Voici comment j'ai résolu le problème:

import Java.util.Random;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.Paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;


public class Minimal extends Application {

private TilePane loadPane;
private ProgressIndicator[] indicators = new ProgressIndicator[9];
private Label loading[] = new Label[9];
private Color[] colors = {Color.BLACK,Color.BLUE,Color.CRIMSON,Color.DARKCYAN,Color.FORESTGREEN,Color.GOLD,Color.HOTPINK,Color.Indigo,Color.Khaki};
private int counter = 0;

@Override
public void start(Stage primaryStage) throws Exception {
    //creating Layout
    final Group root = new Group();                             
    Scene scene = new Scene(root, 400, 400);            
    primaryStage.setScene(scene);
    primaryStage.setResizable(false);
    StackPane waitingPane = new StackPane();
    final ProgressBar progress = new ProgressBar();
    Label load = new Label("loading things...");
    progress.setTranslateY(-25);
    load.setTranslateY(25);
    waitingPane.getChildren().addAll(new Rectangle(400,400,Color.WHITE),load,progress);
    root.getChildren().add(waitingPane);

    //Task for computing the Panels:
    Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                final double prog = i*0.05;
                Platform.runLater(new Runnable() {
                    public void run() {
                        progress.setProgress(prog);
                    }
                });
            }
            return null;
        }
    };

    //stateProperty for Task:
    task.stateProperty().addListener(new ChangeListener<Worker.State>() {

        @Override
        public void changed(ObservableValue<? extends State> observable,
                State oldValue, Worker.State newState) {
            if(newState==Worker.State.SUCCEEDED){
                loadPanels(root);
            }
        }
    });

    //start Task
    new Thread(task).start();

    primaryStage.show();
}

private void loadPanels(Group root) {
    //change to loadPanel:
    root.getChildren().set(0,createLoadPane());

    //Service:
    final Service<Rectangle> RecBuilder = new Service<Rectangle>() {
          @Override protected Task<Rectangle> createTask() {
              return new Task<Rectangle>() {
                  @Override protected Rectangle call() throws InterruptedException {
                      updateMessage("loading rectangle . . .");
                      updateProgress(0, 10);
                      for (int i = 0; i < 10; i++) {
                        Thread.sleep(100);
                      }
                      updateMessage("Finish!");
                      return new Rectangle((380)/3,(380)/3,colors[counter]);
                  }
              };
          }
    };

    //StateListener
    RecBuilder.stateProperty().addListener(new ChangeListener<Worker.State>() {
        @Override public void changed(ObservableValue<? extends Worker.State> observableValue,
                        Worker.State oldState, Worker.State newState) {
            switch (newState) {
            case SCHEDULED:
                break;
            case READY:
            case RUNNING:
                break;
            case SUCCEEDED:
                Rectangle rec = RecBuilder.valueProperty().getValue();
                indicators[counter].progressProperty().unbind();
                loading[counter].textProperty().unbind();
                loadPane.getChildren().set(counter, rec);
                if(counter<8){
                    counter++;
                    nextPane(RecBuilder);
                }
                break;
            case CANCELLED:
            case FAILED:
                loading[counter].textProperty().unbind();
                loading[counter].setText("Failed!");
                if(counter<8){
                    counter++;
                    nextPane(RecBuilder);
                }
                break;
            }
        }
    });

    //begin PanelBuilding:
    nextPane(RecBuilder);
}

private void nextPane(Service<Rectangle> recBuilder) {
    loading[counter].textProperty().bind(recBuilder.messageProperty());
    indicators[counter].visibleProperty().bind(recBuilder.progressProperty().isNotEqualTo(new SimpleDoubleProperty(ProgressBar.INDETERMINATE_PROGRESS)));
    recBuilder.restart();
}

private Node createLoadPane() {
    loadPane = new TilePane(5,5);
    loadPane.setPrefColumns(3);
    loadPane.setPadding(new Insets(5));
    for(int i=0;i<9;i++){
        StackPane waitingPane = new StackPane();
        Rectangle background = new Rectangle((380)/3, (380)/3, Color.WHITE);
        indicators[i] = new ProgressIndicator();
        indicators[i].setPrefSize(50, 50);
        indicators[i].setMaxSize(50, 50);
        indicators[i].setTranslateY(-25);
        indicators[i].setTranslateX(-10);
        loading[i] = new Label();
        loading[i].setTranslateY(25);
        waitingPane.getChildren().addAll(background,indicators[i],loading[i]);
        loadPane.getChildren().add(waitingPane);
    }

    return loadPane;
}

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

}
12
Yggdrasil

Le moyen le plus simple consiste à utiliser des lambdas et du fil.

private void logIn() {
    new Thread(() -> {
        //CONNECT TO WEB AND LOG IN (ON OUT OF FX THREAD)
        boolean loginSuccess = new LogIn(emial.getText(), pass.getText()).execute();

        //DO SOMETHING WITH CONTROLLS ON FX THREAD ACCORDING RESULT OF OVER
        Platform.runLater(() -> {
            if (loginSuccess) {
                info.setText("SUCCESS");
                info.setTextFill(Color.web("#268515"));
            } else {
                info.setText("FAIL");
                info.setTextFill(Color.web("#CD000E"));
            }
        });
    }).start();
}
0
Paweł