2016-11-17 5 views
1

私はこれを数日間苦労しています。スレッド、MVC、バインディング、インターフェイス、そして多くの面白いことを読んだことがありますが、この作品を作る。JavafxどこにラベルをStringPropertyにバインドするのですか

私はちょうどマイルCの内のすべてのファイル一覧表示したい:\をし、変更ラベル 上に表示しかし、私が得るすべては次のとおりです。

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

これは私の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> 

私のメイン:

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); 
    } 
} 

マイ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(); 
    } 

} 

とモデル:

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()); 
     } 

    } 
} 

答えて

2

:それはそのようなものでなければなりません。その結果、FXアプリケーションスレッドでのみモデルのステータスを変更できます。

Platform.runLater(...)を使用して、FXアプリケーションスレッドで実行するコードをスケジュールできます。

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())); 
    } 

} 

またはあなたが(代わりに、結合の)モデルのステータスを持つリスナーを登録し、そこにFXのアプリケーションスレッドに委任することができます:あなたは直接モデルでこれを行うことができますいずれか

@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(); 
} 

後者の解決法では、スレッドが終了したときにリスナーを削除する必要があります(追加の作業が必要です)。それ以外の場合は、ラベルが表示されている間はモデルをガベージコレクションできません。この目的のために、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(); 
} 
1
status.set(directory.getAbsolutePath()); 

は、メインスレッドにする必要があります。ラベルのテキストはUI(ラベルのテキストが変更されます)の変化でモデルのステータス結果を変更する、モデルの状態にバインドされているので

Platform.runLater(() -> status.set(directory.getAbsolutePath())); 
関連する問題