2

我是 FX 的新手,对 Java 不是很陌生,所以请多多包涵!我所拥有的是一个使用由 ObservableList 支持的 JavaFX TableView 的简单应用程序。

当我第一次启动应用程序时,我生成了另一个线程(从 Application 的 start 方法)来监听日志文件的更改并将另一个元素添加到 ObservableList 以便该行在 TableView 中立即可见。我认为在包含 ObservableList 的当前大小的窗口中有一个标签是有益的。我正在尝试将 Label 的 textProperty 绑定到 ObservableList 的大小。我的第一次尝试产生了:

tableSizeLabel.textProperty().bind(Bindings.size((tableView.getItems())).asString());

但我相信我误解了 Bindings API。我意识到 ObservableList.size() 返回一个常规的 int 而不是 ObservableValue ,这是它工作所必需的,但我认为 Bindings 类有一个静态方法可以为我创建一个。

后端模型包含作为静态变量的列表(我省略了监听更改部分):

public class LogFileListener implements Runnable {

private static final ObservableList<SNMPTrap> model = FXCollections.observableArrayList();

@Override
public void run() {
    String line = null;
    try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileLocation)))) {
        while ((line = br.readLine()) != null) {
            model.add(parseTrap(line));
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }

}

public static ObservableList<SNMPTrap> getModel() {
    return model;
}

}

应用类:

public class TableDisplay extends Application {

@Override
public void start(Stage stage) throws Exception {

    new Thread(new LogFileListener()).start();

    Parent root = FXMLLoader.load(getClass().getResource("path/to/FXML"));

    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();

}

控制器类:

public class TableDisplayController implements Initializable {

@FXML
private TableView<SNMPTrap> tableView;
@FXML
private Label tableSizeLabel;

@Override
public void initialize(URL url, ResourceBundle rb) {

    tableView.setItems(LogFileListener.getModel());

    tableSizeLabel.textProperty().bind(Bindings.size((tableView.getItems())).asString());

}

这在外汇中甚至可能吗?我是否需要向整个 ObservableList 添加更改侦听器,然后才使用 getSize() 更新标签?

提前致谢!

4

1 回答 1

9

您的绑定语句对于您想要实现的目标是正确的(将 Label 的文本属性绑定到 ObservableList 的大小)。

您的代码存在并发问题。将会发生的是 LogFileListener 在它自己的线程中运行,更新项目列表,并且项目在 LogFileListener 线程中更新,触发 JavaFX 应用程序线程对场景图的更新。这是一个“坏事”,因为 JavaFX 使用单个 GUI 线程。

您需要做的是确保绑定触发器和表项更新仅发生在 JavaFX 应用程序线程上(并且您可能需要以一种有效的方式执行此操作,以免 JavaFX 事件处理器因太多的小更新而过载来电)。

当前解决方案的一个简单修复是使用以下代码。

final SNMPTrap trap = parseTrap(line);
Platfrom.runLater(new Runnable() {
  @Override public void run() {
    model.add(trap);
  }
});

您的应用程序可能会从更复杂的方法中受益。您可以在 StackOverflow 上搜索有关 Platform.runLater、Task、Service 和JavaFX 并发主题的相关问题,并查看有关 JavaFX 中并发的 Oracle 文档和Task javadoc,以更好地了解所有这些东西是如何工作的以及您的选择是什么.

于 2013-11-11T22:30:30.527 回答