Update eines Labels bei Methodenaufruf einer anderen Klasse

rocknralle

Mitglied
Hallo liebe Java-Community,
ich probiere verzweifelt folgendes Problem zu lösen:

Ich möchte gerne mit der Application (App1.class) starten und diese an die Klasse OtherClass.class in die statische Methode test(App1 app) überführen.
In dieser Methode wird eine while-Schleife vielfach ausgeführt. Innerhalb der Schleife habe ich eine Random-Funktion eingebaut, die beim Wert = 1 wieder zurück auf die Klasse App1 zugreift - und zwar auf die Methode update(String txt).
Dort möchte ich dann (wie auch immer?) den Text des Labels aus App1 verändern und anzeigen lassen. Leider funktioniert das gar nicht. So wie ich das unten vorstelle, wird beim Ausführen nur ein leeres Fenster angezeigt, bis der Algorithmus aus OtherClass.class abgearbeitet ist.

Ich hatte auch schon viel mit Threads sowie property-bindings rumprobiert und bin immer noch zu keiner Lösung gekommen... Das müsste doch ein multithreading-Problem von JavaFX sein?!

Java:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class App1 extends Application{
   
    Label label;
    int count;
   
    public void start(Stage stage){
       
        label = new Label("start...");
        Pane pane = new Pane(label);
        Scene scene = new Scene(pane, 250, 30);
        stage.setScene(scene);
        stage.show();
        
        OtherClass.test(this);
    }
   
    public void update(String txt){
        label.setText(txt);
    }
   
    public static void main(String[] args){
        launch(args);
    }
}

import java.util.Random;
public class OtherClass {
   
    public static void test(App1 app){
       
        Random r = new Random();
        int count=0;
       
        while(count<10000000){
            if(r.nextInt(4)+1 == 1)
                 app.update("Treffer in Runde = "+count);           
            
            count++;
        }
    }
}

Für Hilfe wäre ich äußerst dankbar. 🙂
Viele Grüße
 
Java:
public static void test(MainApp app) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                Random r = new Random();
                int count = 0;

                while (count < 10000000) {
                    if (r.nextInt(4) + 1 == 1) {
                        app.update("Treffer in Runde = " + count);
                    }

                    count++;
                }
            }
        });
    }
 
naja er sagte ja er hat es mit threads probiert....anscheinend nicht mit dem javafx standart und den hab ich in seiner test methode eingefügt.
also wo liegt jetzt das problem @Robat ?
 
Nein, ist es nicht. Man kann auch - wie ich es gern tue - sich über schlechtes Deutsch aufregen. 😉😛

Um dem @rocknralle mal zu erklären, was konkret schief gegangen ist, empfehle ich als erstes folgende Literatur:
https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm

Durch das Studium eben dieser sollte klar werden, dass es nie eine gute Idee ist, Zeit-intensive Arbeit auf dem selben Thread zu verrichten, der sich auch um das Updaten der Oberfläche kümmern soll. Da dieser Teil sozusagen prozedural abläuft, können Updates an der UI erst dann gemacht werden, wenn die Arbeit abgeschlossen ist.
Lösung? Einfach! Verwende Threads! Oder im JavaFx-Context eben Tasks (die auch über Threads gestartet werden) und noch besser: Services.
 
@thet1983 mit Platform#runLater in dem wieder die while-Schleife enthalten ist, wird aber auch wieder die gesamte Arbeit auf dem UI-Thread gemacht. Das ist also auch wieder falsch.

Es müsste also eher ungefähr so aussehen:
Java:
new Thread(() -> {
            Random r = new Random();
            int count = 0;
            while (count < 10000000) {
                if (r.nextInt(4) + 1 == 1) {
                    Platform.runLater(() -> app.update("Treffer in Runde = " + count));
                }
                count++;
            }
        }).start();
Wobei das immer noch eher die hässliche Dampfhammer-Methode ohne gute Kontrolle über die Threads ist - besser ist es, Services und Tasks zu verwenden!
 
kann man das verwenden?

Java:
public static void test(MainApp app) {
        Task<Void> task;
        task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        Random r = new Random();
                        int count = 0;
                        while (count < 10000000) {
                            if (r.nextInt(4) + 1 == 1) {
                                app.update("Treffer in Runde = " + count);
                            }
                            count++;
                        }
                    }
                });
                return null;
            }
        };

        Thread thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();

    }
 
Vielen, vielen Dank für eure Hilfe!!! Wie es aussieht, habe ich das Problem folgendermaßen gelöst:

Java:
import java.util.Random;

import javafx.application.Platform;
import javafx.concurrent.Task;

public class OtherClass {

    public static void test(App1 app) {

        Random r = new Random();
        
        Task task = new Task<Void>() {
           
            @Override
            protected Void call() throws Exception {
                int count = 0;
                while (count < 10000000) {
                    final int counter = count;
                    if (r.nextInt(4) + 1 == 1)
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                app.update("Treffer in Runde = " + counter);
                            }
                        });
                    count++;
                   
                    try {
                         Thread.sleep(10);
                     } catch (InterruptedException interrupted) {
                         if (isCancelled()) {
                             updateMessage("Cancelled");
                             break;
                         }
                     }
                }
                return null;
            }
        };
        Thread thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();
        
    }
}

Mein Fehler war es, dass ich den Task immer in der update-Methode starten wollte...


Erst hatte ich mich gewundert, warum die Anzeige so stolpert, aber dann ist mir wieder eingefallen, dass ich eine Random-Funktion eingebaut habe. :-D
 

Neue Themen


Zurück
Oben