如果链接模型元素具有 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<>());
我的 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<>());