Recherche…


Mise à jour de l'interface utilisateur à l'aide de Platform.runLater

Les opérations de longue durée ne doivent pas être exécutées sur le thread d'application JavaFX, car cela empêche JavaFX de mettre à jour l'interface utilisateur, entraînant une interface utilisateur gelée.

En outre, toute modification d'un Node faisant partie d'un graphe de scène "en direct" doit se produire sur le thread d'application JavaFX. Platform.runLater peut être utilisé pour exécuter ces mises à jour sur le thread d'application JavaFX.

L'exemple suivant montre comment mettre à jour un Node Text plusieurs reprises à partir d'un thread différent:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class CounterApp extends Application {

    private int count = 0;
    private final Text text = new Text(Integer.toString(count));

    private void incrementCount() {
        count++;
        text.setText(Integer.toString(count));
    }

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        root.getChildren().add(text);

        Scene scene = new Scene(root, 200, 200);

        // longrunning operation runs on different thread
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                Runnable updater = new Runnable() {

                    @Override
                    public void run() {
                        incrementCount();
                    }
                };

                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                    }

                    // UI update is run on the Application thread
                    Platform.runLater(updater);
                }
            }

        });
        // don't let thread prevent JVM shutdown
        thread.setDaemon(true);
        thread.start();

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}

Regroupement des mises à jour de l'interface utilisateur

Le code suivant ne répond plus pendant un court instant après le clic du bouton, car trop d'appels Platform.runLater sont utilisés. (Essayez de faire défiler le ListView immédiatement après le clic du bouton.)

@Override
public void start(Stage primaryStage) {
    ObservableList<Integer> data = FXCollections.observableArrayList();
    ListView<Integer> listView = new ListView<>(data);
    
    Button btn = new Button("Say 'Hello World'");
    btn.setOnAction((ActionEvent event) -> {
        new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                final int index = i;
                Platform.runLater(() -> data.add(index));
            }
        }).start();
    });

    Scene scene = new Scene(new VBox(listView, btn));

    primaryStage.setScene(scene);
    primaryStage.show();
}

Pour éviter cela au lieu d'utiliser un grand nombre de mises à jour, le code suivant utilise AnimationTimer pour exécuter la mise à jour une seule fois par image:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.AnimationTimer;

public class Updater {

    @FunctionalInterface
    public static interface UpdateTask {

        public void update() throws Exception;
    }

    private final List<UpdateTask> updates = new ArrayList<>();

    private final AnimationTimer timer = new AnimationTimer() {

        @Override
        public void handle(long now) {
            synchronized (updates) {
                for (UpdateTask r : updates) {
                    try {
                        r.update();
                    } catch (Exception ex) {
                        Logger.getLogger(Updater.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                updates.clear();
                stop();
            }
        }
    };

    public void addTask(UpdateTask... tasks) {
        synchronized (updates) {
            updates.addAll(Arrays.asList(tasks));
            timer.start();
        }
    }

}

qui permet de regrouper les mises à jour à l'aide de la classe Updater :

private final Updater updater = new Updater();

...

        // Platform.runLater(() -> data.add(index));
        updater.addTask(() -> data.add(index));

Comment utiliser le service JavaFX

Au lieu d'exécuter des tâches intensives dans JavaFX Thread cela doit être fait dans un Service . Alors, qu'est-ce qu'un service ?

Un service est une classe qui crée un nouveau Thread chaque fois que vous le lancez et lui transmet une tâche pour effectuer un travail. Le service peut renvoyer ou non une valeur.

Vous trouverez ci-dessous un exemple type de service JavaFX qui effectue un travail et retourne une Map<String,String>( ):

public class WorkerService extends Service<Map<String, String>> {

    /**
     * Constructor
     */
    public WorkerService () {

        // if succeeded
        setOnSucceeded(s -> {
            //code if Service succeeds
        });

        // if failed
        setOnFailed(fail -> {
            //code it Service fails
        });

        //if cancelled
        setOnCancelled(cancelled->{
            //code if Service get's cancelled
        });
    }

    /**
    * This method starts the Service
    */
    public void startTheService(){
        if(!isRunning()){
           //...
           reset();
           start();
        }

    }

    @Override
    protected Task<Map<String, String>> createTask() {
        return new Task<Map<String, String>>() {
            @Override
            protected Void call() throws Exception {

                    //create a Map<String, String>
                    Map<String,String> map  = new HashMap<>();

                   //create other variables here

                   try{
                        //some code here
                        //.....do your manipulation here

                        updateProgress(++currentProgress, totalProgress);
                    }

                } catch (Exception ex) {                  
                    return null; //something bad happened so you have to do something instead of returning null
                }

                return map;
            }
        };
    }

}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow