如果链接模型元素具有 ListProperty<>,如何更新 TableView

How to update TableView if linked model elements have ListProperty<>

我的 TableView 包含的元素具有几个使用属性公开的属性。一个元素由一个称为 numbers 的数字列表、称为 numbersSize 的列表的大小和一个称为 number 的数字组成。 UI 中不会更新的有问题的是数字列表。

Table 列定义如下

@FXML private TableView<Element> table;
@FXML private TableColumn<Element, Integer> numberCol;
@FXML private TableColumn<Element, Integer> numbersSizeCol;
@FXML private TableColumn<Element, List<Integer>> numbersCol;

元素 class 看起来像这样

private ObjectProperty<Integer> number;
private IntegerProperty numbersSize;
private ListProperty<Integer> numbers;

public ObjectProperty<Integer> numberProperty() { return number;    } 
public IntegerProperty numbersSizeProperty() { return numbersSize; }    
public ListProperty<Integer> numbersProperty() { return numbers; }

number 是 ObjectProperty 类型,因为我希望 null 也是一个可能的值,而仅使用 IntegerProperty 是不可能的。元素 class 的大小 属性 直接链接到数字列表的 sizeProperty 就像这样

numbersSize.bind(numbers.sizeProperty());

table和模型的连接是这样完成的

numberCol.setCellValueFactory(new PropertyValueFactory<>("number"));
numbersSizeCol.setCellValueFactory(new PropertyValueFactory<>("numbersSize"));
numbersCol.setCellValueFactory(new PropertyValueFactory<>("numbers"));

我计划在后台线程中对 table 元素进行一些时间密集型工作,在开始之前,我测试了模型中的更改是否正确反映在 table 像这样

List<Integer> l = new ArrayList<>();
l.add(1);
l.add(2);
l.add(3);
ObservableList<Integer> o = FXCollections.observableList(l);
Element e = new Element(0, o);
elementController.getElementList().add(e);

startBtn.setOnAction(e -> {         
        new Thread(() ->  {
            Element e = elementController.getElementList().get(0);
            Platform.runLater(() -> {
                e.numberProperty().set(1234);
                e.numbersProperty().add(9);
            });
        }).start();         
    });

现在发生的真正奇怪的事情是:

数字列显示 1234 而不是通过元素构造函数指定的 0,到目前为止,对 table.

的更改很好

数字大小列显示适合的 4,因为元素是使用 1、2、3 的列表初始化的,并且添加了 9,到目前为止,对 table 的更改很好。

但是数字列表栏仍然显示[1,2,3],缺少9,这是为什么? TableColumn 链接到 ListProperty,ListProperty 由包裹在 ObservableList 中的 ArrayList 支持...,难道不应该检测到对该列表的更改并将其传播回 table 吗?

如果我将两个元素添加到 table 并添加代码以删除 Platform.runLater(...) 中的第二个元素,则数字列显示 [1,2, 3,9]。所以问题似乎是 number 和 numbersSize 总是正确显示,但只有当元素是 table 视图后端的 added/removed 时列表数字才能正确显示。

然而,我想要的是 table 视图中元素的数量完全不变(一旦它最初被填充)。我想对每个元素的数字列表执行 additions/removals,但它只是那些在更改时不会出现在 UI 中的元素:/

ListProperty<Integer> 是一个 Property<ObservableList<Integer>> 并实现了 ObservableList<Integer>。对 属性 本身的修改应用于存储在 属性.

中的值

这意味着

e.numbersProperty().add(9);

效果相同
e.numbersProperty().get().add(9);

此操作不会替换 属性 的值,因此 TableCell 不会更新。

要更新 TableCell 即使 属性 中的列表已更新,您可以实施自定义 cellFactory:

public class ObservableItemTableCell<S, T extends Observable> extends TableCell<S, T> {
    final InvalidationListener l = o -> setText(getItem().toString());
    final WeakInvalidationListener listener = new WeakInvalidationListener(l);

    @Override
    protected void updateItem(T item, boolean empty) {
        // remove old listener
        T oldItem = getItem();
        if (oldItem != null) {
            oldItem.removeListener(listener);
        }

        super.updateItem(item, empty);

        if (empty || item == null) {
            setText("");
        } else {
            setText(item.toString());
            item.addListener(listener);
        }
    }
}
@FXML private TableColumn<Element, ObservableList<Integer>> numbersCol;
numbersCol.setCellValueFactory(new PropertyValueFactory<>("numbers"));
numbersCol.setCellFactory(col -> new ObservableItemTableCell<>());