简体   繁体   English

JavaFX:将textProperty从Label绑定到ObservableList的大小

[英]JavaFX: Bind the textProperty from a Label to the size of an ObservableList

I am new to FX, not so new to Java, so please bear with me! 我是FX的新手,而不是Java的新手,所以请多多包涵! What I have is a simple application using a JavaFX TableView backed by an ObservableList. 我所拥有的是一个使用由ObservableList支持的JavaFX TableView的简单应用程序。

When I first launch the application I spawn another Thread (from the start method of Application) to listen to changes to a log file and add another element to the ObservableList so that the row is immediately visable in the TableView. 当我第一次启动应用程序时,我产生了另一个线程(来自Application的start方法)来侦听对日志文件的更改,并将另一个元素添加到ObservableList中,以便可以在TableView中立即看到该行。 I thought it would be beneficial to have a Label in the window that contains the current size of the ObservableList. 我认为在包含ObservableList当前大小的窗口中放置一个Label将是有益的。 I am attempting to bind the textProperty of the Label to the size of the ObservableList. 我试图将Label的textProperty绑定到ObservableList的大小。 My first attempt yielded: 我的第一次尝试产生了:

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

But I believe I am misunderstanding the Bindings API. 但是我相信我对Bindings API有误解。 I realize that ObservableList.size() returns a regular int instead of an ObservableValue which is necessary for it to even work, but I thought the Bindings class had a static method to create one for me. 我意识到ObservableList.size()返回一个常规的int而不是它正常工作所必需的ObservableValue,但是我认为Bindings类有一个静态方法为我创建一个。

The back-end model contains the list as a static variable (I left out the listen-for-changes part): 后端模型将列表包含为静态变量(我省略了“侦听变更”部分):

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

} }

The Application Class: 应用类别:

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

}

The Controller Class: 控制器类:

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

}

Is this even possible in FX? 在FX中甚至可能吗? Do I need to add a change listener to the entire ObservableList instead and only then update the Label with getSize()? 我是否需要向整个ObservableList添加更改侦听器,然后才使用getSize()更新Label?

Thanks in advance! 提前致谢!

Your binding statement is correct for what you want to achieve (binding text property of a Label to the size of an ObservableList). 您的绑定语句对于您要实现的目标是正确的(将Label的text属性绑定到ObservableList的大小)。

Your code has concurrency issues. 您的代码存在并发问题。 What will happen is the LogFileListener runs in its own thread, updates the list of items and the items are updated in the LogFileListener thread, triggering updates to the scene graph off of the JavaFX application thread. 将会发生的是LogFileListener在其自己的线程中运行,更新了项目列表,并且这些项目在LogFileListener线程中进行了更新,从而触发了JavaFX应用程序线程对场景图的更新。 This is a "bad thing" as JavaFX uses a single GUI thread. 这是一件“坏事”,因为JavaFX使用单个GUI线程。

What you need to do is ensure that the binding trigger and table item updates only occur on the JavaFX application thread (and you might need to do so in an efficient way such that you don't overload the JavaFX event processor with too many little update calls). 您需要做的是确保绑定触发器和表项更新仅在JavaFX应用程序线程上发生(并且您可能需要以一种有效的方式进行更新,以免JavaFX事件处理器不会因太多更新而过载)电话)。

A simple fix for your current solution is to use the following code. 当前解决方案的一个简单解决方案是使用以下代码。

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

Your application might potentially benefit from a more sophisticated approach. 您的应用程序可能会受益于更复杂的方法。 You can search StackOverflow for related questions on Platform.runLater, Task, Service and JavaFX concurrency topics as well as review the Oracle documentation on concurrency in JavaFX and the Task javadoc to gain a better understanding of how all this stuff works and what your options are. 您可以在StackOverflow上搜索有关Platform.runLater,Task,Service和JavaFX并发性主题的相关问题,还可以查看有关JavaFX和Task javadoc并发性的Oracle文档,以更好地理解所有这些工作原理以及您的选择是什么。 。

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

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