JavaFX: UI aus Thread updaten

JavaFX 2.2Zunächst möchte ich darauf hinweisen, dass man parallele oder rechenintensive Aufgaben nicht immer in einen eigenständigen Thread auslagern muss. JavaFX bietet seit Version 2.0 mehrere Möglichkeiten an, Aufgabe effizient auszulagern. In dem Artikel „Concurrency in JavaFX“ wird sehr anschaulich die Verwendung von den Klassen javafx.concurrent.Task und javafx.concurrent.Service erklärt. Hier ist der Link zur API der Task-Klasse.
Zurück zum Thema: In diesem Beitrag erkläre ich, wie man seine JavaFX UI von einem beliebigen Thread aus updaten kann. Hierzu nutze ich die vom Framework bereitgestellte Funktion „Platform.runLater()„.

 

Ausgangspunkt

Um die Verwendung der Funktion besser erklären zu können, habe ich eine kleine Beispiel-Anwendung programmiert.
Das Programm zeigt dynamisch aktuelle Statusmeldungen in einer ListView an. Der folgende Screenshot zeigt die UI:

 

Die Thread-Klasse

Die gesamte UI auf einmal upzudaten ist in einem externen Thread leider nicht möglich. Teilweise aktualisieren sich Komponenten selbst, jedoch kann man mit folgenden Zeilen jede Komponenten-Referenz aktualisieren:

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        // entsprechende UI Komponente updaten
    }
});

Da ich in meinem Beispiel nur die ListView updaten will, sieht die Thread-Klasse etwas leer aus.

package de.axxg;

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.scene.control.ListView;

/**
 *
 * @author AxxG
 * @see http://blog.axxg.de/
 */
public class WorkerThread extends Thread{
    
    private ListView<String> list = null;

    public WorkerThread(ListView<String> list, int count) {
        setDaemon(true);
        setName("Thread " + count);
        this.list = list;
    }

    @Override
    public void run() {

        while (!this.isInterrupted()) {
            
            // UI updaten
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    // entsprechende UI Komponente updaten
                    list.getItems().add(0, getName() + " sagt Hallo!");
                }
            });

            // Thread schlafen
            try {
                // fuer 3 Sekunden
                sleep(TimeUnit.SECONDS.toMillis(3));
            } catch (InterruptedException ex) {
                Logger.getLogger(WorkerThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }


    }

}

 

Die Main-Klasse

Um jetzt noch das Rätsel zu lüft, wo ich die Komponenten-Referenz her habe, kommt hier die Main-Klasse:

package de.axxg;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 *
 * @author AxxG
 * @see http://blog.axxg.de/
 */
public class UIThreadUpdate extends Application {
    
    private ListView<String> list;
    private int count = 0;
    
    
    @Override
    public void start(Stage primaryStage) {
        
        // Java 7 Style;-)
        list = new ListView<>();
        list.setPrefWidth(200);

        // Button erstellen
        Button btn = new Button();
        btn.setText("Starte einen Thread");
        btn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                WorkerThread myThread = new WorkerThread(list, count);
                myThread.start();
                
                count++;
            }
        });
        
        
        // Layout der Anwendung
        VBox myBox = new VBox();
        // Komponenten dem Layout hinzufuegen
        myBox.setAlignment(Pos.CENTER);
        myBox.getChildren().add(btn);
        myBox.getChildren().add(list);
        
        // alles zusammensetzen
        StackPane root = new StackPane();
        root.getChildren().add(myBox);   
        Scene scene = new Scene(root, 300, 250);
        
        // Stage konfiguieren
        primaryStage.setTitle("AxxG - UI Thread Update");
        primaryStage.setScene(scene);
        primaryStage.show();
    }


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

 

Resultat

 

Download

Java Version JDK 1.7.0_07 Inhalt gepacktes Projekt
JavaFX Version 2.2.1 Größe 146 KB
IDE NetBeans IDE 7.2 Endung *.zip
Preis kostenlos Lizenz Creative Commons Lizenzvertrag

[download id=“14″ format=“2″]

 

Copyright © 2012 AxxG – Alexander Gräsel




7 Antworten : “JavaFX: UI aus Thread updaten”

  1. Steven sagt:

    Danke, Alexander! Ich habe gerade nach langem Nachdenken und Suchen den Fehler in meiner Anwendung und die Bedeutung von Platform.runLater verstanden. Schöner Artikel!

  2. Bernd sagt:

    Du glaubst gar nicht wie sehr mir dein Code Schnipsel grad geholfen hat -.-! Vielen Dank fürs online stellen! Ansonsten säße ich jetzt wohl noch den ganzen Tag dran den Mist zu recherchieren. ~ ein nicht mehr ganz so frustrierter Informatik Student aus der Stadt die es nicht gibt.

  3. Thanks a lot, this made my day. Your article was much more clear to me than the official ones on the Oracle website or those from Stackoverflow.

  4. David G. sagt:

    Ich war seit 2 Stunden am recherchieren… UND ENDLICH HAB ICH MEINE LÖSUNG. Danke vielmals 🙂

Kommentar verfassen

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.