繁体   English   中英

Javafx将标签绑定到StringProperty的位置

[英]Javafx where to bind labels to StringProperty

我已经为此苦苦挣扎了好几天,我已经读到了有关线程,MVC,绑定,接口和许多有趣的东西的信息,但是我只是不能以适当的方式将它们放在一起来完成这项工作。

我只想列出mi 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);
    }
}

我的控制器:

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

    }
}

由于标签的文本绑定到模型的状态,因此更改模型的状态会导致UI发生更改(标签的文本会更改)。 因此,您只能在FX Application Thread上更改模型的状态。

您可以使用Platform.runLater(...)安排代码在FX Application Thread上运行。 您可以直接在模型中执行以下操作:

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 Application Thread:

@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();
}
status.set(directory.getAbsolutePath()); 

应该在主线程中。 应该是这样的:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM