我是FX新手,对Java不是新手,所以请耐心等待!我所拥有的是一个使用由ObservableList支持的JavaFX TableView的简单应用程序。
当我第一次启动应用程序时,我会生成另一个Thread(来自Application的start方法)来监听对日志文件的更改,并将另一个元素添加到ObservableList中,以便在TableView中可以立即看到该行。我认为在窗口中包含一个包含ObservableList当前大小的Label是有益的。我试图将Label的textProperty绑定到ObservableList的大小。我的第一次尝试产生了:
tableSizeLabel.textProperty().bind(Bindings.size((tableView.getItems())).asString());
但我相信我误解了Bindings API。我意识到ObservableList.size()返回一个常规的int而不是ObservableValue,它甚至可以工作,但我认为Bindings类有一个静态方法为我创建一个。
后端模型将列表作为静态变量包含在内(我省略了listen-for-changes部分):
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());
}
这在FX中甚至可能吗?我是否需要向整个ObservableList添加更改侦听器,然后才用getSize()更新Label?
提前致谢!
答案 0 :(得分:7)
您的绑定语句对于您想要实现的目标是正确的(将Label的text属性绑定到ObservableList的大小)。
您的代码存在并发问题。会发生什么是LogFileListener在其自己的线程中运行,更新项目列表并在LogFileListener线程中更新项目,从而触发JavaFX应用程序线程的场景图形更新。这是一个“坏事”,因为JavaFX使用单个GUI线程。
您需要做的是确保绑定触发器和表项更新仅发生在JavaFX应用程序线程上(您可能需要以有效的方式执行此操作,以便您不会使JavaFX事件处理器过载许多小更新电话)。
对当前解决方案的一个简单修复是使用以下代码。
final SNMPTrap trap = parseTrap(line);
Platfrom.runLater(new Runnable() {
@Override public void run() {
model.add(trap);
}
});
您的应用程序可能会受益于更复杂的方法。您可以在Platform.runLater,任务,服务和JavaFX concurrency主题上搜索StackOverflow以获取相关问题,并在JavaFX和Task javadoc中查看有关并发性的Oracle文档,以便更好地了解所有这些内容工作和你的选择。