JavaFX GUI-Elemente erstellen, nachdem die GUI geladen hat

marc_book

Mitglied
Hallo,
ich programmiere gerade ein kleines Tool, welches die Position aller Clients auf einer Karte(Bild) anzeigen soll. Da beliebig viele Clients online sein können, möchte ich gerne jedes mal, wenn ein Client verbindet ein neues Label an einer beliebigen Position erstellen. Gibt es eine Möglichkeit, Labels nach dem Erstellen der GUI noch zu laden? Ich möchte nämlich nicht 200 Labels definieren.
 
Du kannst doch einfach im entsprechenden Controller per "Label l = new Label()" ein neues Label erzeugen und der entsprechenden Komponenten hinzufügen.
 
Hallo,
danke für deine Antwort, ich habe es nun wie folgt versucht, jedoch habe bekomme ich dabei folgen den Fehler.

Main.java
Java:
package application;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        javafx.application.Application.launch(Gui.class);
    }
}

Gui.java
Java:
package application;

public class Gui extends Application {  
    public static TextArea debugConsole = new TextArea("Console gestartet!");
//    public static Label p1, p2, p3, p4, p5 = new Label("");
  
    public void start(Stage primaryStage) {
              
        MenuBar menuBar = new MenuBar();
      
        Menu menuFile = new Menu("File");
        MenuItem itemSave = new MenuItem("Save");
        menuFile.getItems().addAll(itemSave);
      
        Menu menuOptions = new Menu("Options");
        MenuItem itemUpdates = new MenuItem("Search for Updates");
        MenuItem itemSettings = new MenuItem("Settings");
        MenuItem itemDebugConsole = new MenuItem("Debug Console");
        menuOptions.getItems().addAll(itemUpdates, itemSettings, itemDebugConsole);
      
        menuBar.getMenus().addAll(menuFile, menuOptions);
      
      
      
        ScrollPane scroll = new ScrollPane();
        scroll.setPannable(true);
        BorderPane border = new BorderPane();
        Thread t = new Thread(new Server(border));
        t.start();
      
        Image image = new Image(getClass().getResourceAsStream("/maps/altis.png"));
        ImageView imgMap = new ImageView(image);
        scroll.setContent(imgMap);
      
        final DoubleProperty zoomProperty = new SimpleDoubleProperty(400);
        zoomProperty.addListener(new InvalidationListener() {
          
        @Override
        public void invalidated(Observable observable) {
            imgMap.setFitWidth(zoomProperty.get());
            imgMap.setFitHeight(zoomProperty.get());          
            }
        });
      
        imgMap.setCache(true);
        imgMap.preserveRatioProperty().set(true);
      
        border.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {

            @Override
            public void handle(ScrollEvent event) {
                if (event.getDeltaY() > 0) {
                    zoomProperty.set(zoomProperty.get() * 1.1);
                } else if (event.getDeltaY() < 0) {
                    zoomProperty.set(zoomProperty.get() / 1.1);
                }              
            }
        });
      
        border.setTop(menuBar);
        border.setCenter(scroll);
        border.setBottom(debugConsole);
        Scene scene = new Scene(border, 400, 500);
        primaryStage.setScene(scene);
        primaryStage.setTitle("marc_book's Navipad");
      
        primaryStage.show();
      
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent t) {
                Platform.exit();
                System.exit(0);
            }
        });
    }
  
    public static void setDebugConsole(BorderPane border, String message) {
//        String oldText = debugConsole.getText() + "\n";
//        debugConsole.setText(oldText + message);
        if(message.substring(0,1).equals("B")) {
            System.out.println(message);
        }
        Label l = new Label(message);
        border.setRight(l);
    }
}

Server.java
Java:
package application;

public class Server implements Runnable {
    public BorderPane border;
    public Server(BorderPane border) {
        this.border = border;
    }

    @SuppressWarnings("resource")
    public void run() {
        DatagramSocket serverSocket = null;
        try {
            serverSocket = new DatagramSocket(5004);
        } catch (SocketException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        byte[] receiveData = new byte[1024];
        @SuppressWarnings("unused")
        byte[] sendData = new byte[1024];
      
        System.out.println("Server hört");
        while (true) {
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            try {
                serverSocket.receive(receivePacket);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String sentence = new String( receivePacket.getData());
            Gui.setDebugConsole(border, sentence);
        }      
    }
}

Fehler.
Code:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Unknown Source)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(Unknown Source)
    at javafx.scene.Parent$2.onProposedChange(Unknown Source)
    at com.sun.javafx.collections.VetoableListDecorator.add(Unknown Source)
    at javafx.scene.layout.BorderPane$BorderPositionProperty.invalidated(Unknown Source)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source)
    at javafx.beans.property.ObjectPropertyBase.set(Unknown Source)
    at javafx.scene.layout.BorderPane.setRight(Unknown Source)
    at application.Gui.setDebugConsole(Gui.java:109)
    at application.Server.run(Server.java:41)
    at java.lang.Thread.run(Unknown Source)
 
Zuletzt bearbeitet von einem Moderator:
Die Message in der Exception ist entscheidend: "Not on FX application thread; currentThread = Thread-4"

Du versuchst die GUI von einem Nicht-UI-Thread upzudaten. Das passiert, wenn du versuchst, die "Methode Gui.setDebugConsole(border, sentence);" in deinem "Server"-Thread aufzurufen (siehe Stacktrace).
Lösung: Platform#runLater(Runnable) -> wrappe "Label l = new Label(message); border.setRight(l);" in das Runnable, das du an Platform#runLater übergibst.

Noch zu deinem Code allgemein:
Ich muss sagen, dass ich es nicht sehr sauber finde, dieses Update per statischer Methode zu machen. Oder dass du dazu vorher das BorderPane in den Server geben musst.
Es wäre hier besser den "Server"-Thread von der GUI agnostisch zu machen. Du könntest dir ein zentrales Model vorhalten und dort solche Messages reinpumpen. Die GUI würde auf die Änderungen an diesem Model hören.
Dass du wahrscheinlich dieses Model per Singleton Pattern vorhalten müsstest, ist etwas anderes (liesse sich mit Dependency Injection wenigstens "hübsch" machen).

Grundsätzlich musst du einfach immer bedenken, dass wenn du von einem anderen als dem Main-Thread (auch UI-Thread genannt) die Oberfläche aktualisieren möchtest, musst du Platform#runLater verwenden.
 
Ok, danke schon mal für deine Antwort, ich werde mich das Wochenende mal daran machen, den Code so wie du vorschlägst aufzubauen. Zum Glück gibts git 😀.
 

Zurück
Oben