2016-11-17 11 views
1

Ich habe seit mehreren Tagen damit zu kämpfen, ich habe über Threads, MVC, Bindungen, Schnittstellen und viele interessante Dinge gelesen, aber ich kann sie alle nicht in der richtigen Weise zusammensetzen mach das so.Javafx, wo Labels an StringProperty gebunden werden

Ich möchte nur alle Dateien in mi c aufzulisten: \ und zeigt sie auf eine sich ändernde Label Aber alles, was ich bekommen ist:

Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4 

Das ist mein FXML:

<?import javafx.scene.control.Button?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.layout.AnchorPane?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 


<GridPane alignment="center" hgap="10" prefHeight="200.0" prefWidth="401.0" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="sample.Controller"> 
    <columnConstraints> 
     <ColumnConstraints /> 
    </columnConstraints> 
    <rowConstraints> 
     <RowConstraints /> 
    </rowConstraints> 
    <children> 
     <AnchorPane prefHeight="200.0" prefWidth="368.0"> 
     <children> 
      <Button fx:id="start" layoutX="159.0" layoutY="35.0" mnemonicParsing="false" onAction="#displayFiles" text="Start" /> 
      <Label fx:id="fileLabel" layoutX="20.0" layoutY="100.0" prefHeight="21.0" prefWidth="329.0" text="This label must change on iteration" /> 
     </children> 
     </AnchorPane> 
    </children> 
</GridPane> 

My Main:

import javafx.application.Application; 
import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Main extends Application { 

    @Override 
    public void start(Stage primaryStage) throws Exception{ 
     Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); 
     primaryStage.setTitle("Dummy App"); 
     primaryStage.setScene(new Scene(root)); 
     primaryStage.setResizable(false); 


     primaryStage.show(); 
    } 


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

My C ontroller:

import javafx.event.ActionEvent; 
import javafx.fxml.FXML; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 

public class Controller { 
    @FXML 
    Button start; 

    @FXML 
    Label fileLabel; 

    @FXML 
    void displayFiles(ActionEvent event) throws Exception{ 
     Model model = new Model(); 

     //BINDING 
     fileLabel.textProperty().bind(model.status); 

     Thread thread = new Thread(model); 

     thread.start(); 
    } 

} 

Und das Modell:

import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

import java.io.File; 

/** 
* Created by R00715649 on 16-Nov-16. 
*/ 
public class Model implements Runnable { 
    File rootDirectory = new File("C:/"); 
    StringProperty status = new SimpleStringProperty("Starting scan..."); 

    @Override 
    public void run() { 
     try{ 

      File[] fileList = rootDirectory.listFiles(); 
      for (File f:fileList){ 
       processDirectory(f); 
      } 

     }catch (Exception e){ 

     } 
    } 

    void processDirectory (File directory){ 
     if (directory.isDirectory()){ 
      File[] fileList = directory.listFiles(); 
      for (File f:fileList){ 
       processDirectory(f); 
      } 

     }else{ 
      System.out.println(directory.getAbsolutePath()); 
      status.set(directory.getAbsolutePath()); 
     } 

    } 
} 

Antwort

2

Da der Text des Etiketts auf den Status des Modells gebunden ist, den Status führt zu einer Änderung in der Benutzeroberfläche des Modells zu ändern (der Text des Etiketts ändern). Folglich können Sie nur den Status des Modells im FX-Anwendungs-Thread ändern.

Sie können festlegen, dass Code auf dem FX-Anwendungs-Thread mit Platform.runLater(...) ausgeführt wird. Sie können dies entweder direkt im Modell tun:

void processDirectory (File directory){ 
    if (directory.isDirectory()){ 
     File[] fileList = directory.listFiles(); 
     for (File f:fileList){ 
      processDirectory(f); 
     } 

    }else{ 
     System.out.println(directory.getAbsolutePath()); 
     Platform.runLater(() -> status.set(directory.getAbsolutePath())); 
    } 

} 

oder Sie können mit dem FX Anwendung Faden einen Zuhörer mit dem Modellstatus (anstelle der Bindung) und delegieren registrieren:

@FXML 
void displayFiles(ActionEvent event) throws Exception{ 
    Model model = new Model(); 

    ChangeListener<String> listener = (obs, oldStatus, newStatus) -> fileLabel.setText(newStatus); 
    model.status.addListener(listener); 

    Thread thread = new Thread(model); 

    thread.start(); 
} 

Bei der letzten Lösung möchten Sie den Listener wahrscheinlich entfernen, wenn der Thread fertig ist (was zusätzliche Arbeit erfordert), da andernfalls das Modell nicht als Garbage Collected erfasst werden kann, während das Label noch angezeigt wird. Zu diesem Zweck könnten Sie einen Task:

@FXML 
void displayFiles(ActionEvent event) throws Exception{ 
    Model model = new Model(); 

    Task<Void> task = new Task<Void>() { 
     @Override 
     public Void call() { 
      model.status.addListener((obs, oldStatus, newStatus) -> updateMessage(newStatus)); 
      model.run(); 
      return null ; 
     } 
    }; 

    fileLabel.textProperty().bind(task.messageProperty()); 

    task.setOnSucceeded(e -> fileLabel.textProperty().unbind()); 

    new Thread(task).start(); 
} 
in Betracht ziehen
1
status.set(directory.getAbsolutePath()); 

sollte im Hauptthread sein. Es sollte so etwas sein:

Platform.runLater(() -> status.set(directory.getAbsolutePath())); 
Verwandte Themen