oldItems.equals(newItems) 周围似乎有几个单独的问题
RT-22463的第一部分:即使调用 items.clear(),tableView 也不会更新
// refresh table
table.getItems().clear();
table.setItems(listEqualToOld);
那是固定的。在设置新列表之前清除旧项目会清除所有旧状态,从而刷新表。这不起作用的任何示例都可能是回归。
仍然不起作用的是在没有先清除的情况下重新设置项目
// refresh table
table.setItems(listEqualToOld);
如果表格显示的属性未涉及到项目的平等决定(参见RT-22463或Aubin 中的示例)并且希望被RT-39094覆盖,那将是一个问题
更新:RT-39094后者也已修复,适用于 8u40!应该会在几周内冒泡进入 ea,推测 u12 之类的。
技术原因似乎是单元格实现中的相等检查:在实际调用 updateItem(T, boolean) 之前检查项目的更改是为了解决性能问题。合理,只是硬编码“更改” == old.equals(new) 在某些情况下会造成问题。
一个对我来说很好的解决方法(没有正式测试!)是一个自定义的 TableRow,如果需要身份检查,它会跳进去:
/**
* Extended TableRow that updates its item if equal but not same.
* Needs custom skin to update cells on invalidation of the
* item property.<p>
*
* Looks ugly, as we have to let super doing its job and then
* re-check the state. No way to hook anywhere else into super
* because all is private. <p>
*
* Super might support a configuration option to check against
* identity vs. against equality.<p>
*
* Note that this is _not_ formally tested! Any execution paths calling
* <code>updateItem(int)</code> other than through
* <code>indexedCell.updateIndex(int)</code> are not handled.
*
* @author Jeanette Winzenburg, Berlin
*/
public class IdentityCheckingTableRow<T> extends TableRow<T> {
@Override
public void updateIndex(int i) {
int oldIndex = getIndex();
T oldItem = getItem();
boolean wasEmpty = isEmpty();
super.updateIndex(i);
updateItemIfNeeded(oldIndex, oldItem, wasEmpty);
}
/**
* Here we try to guess whether super updateIndex didn't update the item if
* it is equal to the old.
*
* Strictly speaking, an implementation detail.
*
* @param oldIndex cell's index before update
* @param oldItem cell's item before update
* @param wasEmpty cell's empty before update
*/
protected void updateItemIfNeeded(int oldIndex, T oldItem, boolean wasEmpty) {
// weed out the obvious
if (oldIndex != getIndex()) return;
if (oldItem == null || getItem() == null) return;
if (wasEmpty != isEmpty()) return;
// here both old and new != null, check whether the item had changed
if (oldItem != getItem()) return;
// unchanged, check if it should have been changed
T listItem = getTableView().getItems().get(getIndex());
// update if not same
if (oldItem != listItem) {
// doesn't help much because itemProperty doesn't fire
// so we need the help of the skin: it must listen
// to invalidation and force an update if
// its super wouldn't get a changeEvent
updateItem(listItem, isEmpty());
}
}
@Override
protected Skin<?> createDefaultSkin() {
return new TableRowSkinX<>(this);
}
public static class TableRowSkinX<T> extends TableRowSkin<T> {
private WeakReference<T> oldItemRef;
private InvalidationListener itemInvalidationListener;
private WeakInvalidationListener weakItemInvalidationListener;
/**
* @param tableRow
*/
public TableRowSkinX(TableRow<T> tableRow) {
super(tableRow);
oldItemRef = new WeakReference<>(tableRow.getItem());
itemInvalidationListener = o -> {
T newItem = ((ObservableValue<T>) o).getValue();
T oldItem = oldItemRef != null ? oldItemRef.get() : null;
oldItemRef = new WeakReference<>(newItem);
if (oldItem != null && newItem != null && oldItem.equals(newItem)) {
forceCellUpdate();
}
};
weakItemInvalidationListener = new WeakInvalidationListener(itemInvalidationListener);
tableRow.itemProperty().addListener(weakItemInvalidationListener);
}
/**
* Try to force cell update for equal (but not same) items.
* C&P'ed code from TableRowSkinBase.
*/
private void forceCellUpdate() {
updateCells = true;
getSkinnable().requestLayout();
// update the index of all children cells (RT-29849).
// Note that we do this after the TableRow item has been updated,
// rather than when the TableRow index has changed (as this will be
// before the row has updated its item). This will result in the
// issue highlighted in RT-33602, where the table cell had the correct
// item whilst the row had the old item.
final int newIndex = getSkinnable().getIndex();
for (int i = 0, max = cells.size(); i < max; i++) {
cells.get(i).updateIndex(newIndex);
}
}
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(IdentityCheckingListCell.class.getName());
}
// usage
table.setRowFactory(p -> new IdentityCheckingTableRow());
请注意,TableCell 具有类似的硬编码相等性检查,因此如果自定义行不够,可能需要使用具有类似解决方法的自定义 TableCell(虽然没有遇到需要这样做的示例)