基本值列表由(更改)谓词筛选。FilteredList被映射到TreeItems,然后这个结果列表被用作根TreeItem的子列表。
当在TreeTableView上进行选择时,然后谓词发生更改,访问所选的项将产生一个NullPointerException。
在我看来,更改中包含的项是null。这个粗糙的概念有设计缺陷吗?
对于类和ListView,这种情况不会发生。
我尝试使用https://github.com/TomasMikula/EasyBind生成一个MCVE,用于映射:
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.fxmisc.easybind.EasyBind;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Spinner;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
// fields protect bound lists from GC
private ObservableList<DataItem> itemizedDataPool;
private FilteredList<Data> filteredDataPool;
private ObservableList<Data> selectedData;
static class Data {
final int value;
public Data(int value) {
this.value = value;
}
}
static class DataItem extends TreeItem<Data> {
final Data data;
public DataItem(Data data) {
this.data = data;
}
}
@Override
public void start(Stage primaryStage) throws IOException {
List<Data> dataPool = new ArrayList<Data>();
for (int i = 1; i < 20; i++) {
dataPool.add(new Data(i));
}
filteredDataPool = new FilteredList<>(FXCollections.observableArrayList(dataPool));
TreeTableView<Data> listView = createTreeTableView();
Spinner<?> lowerBoundSelector = createLowerBoundFilter();
Label sumLabel = createSummarizingLabel(listView.getSelectionModel().getSelectedItems());
Parent root = new VBox(listView, lowerBoundSelector, sumLabel);
Scene scene = new Scene(root, 768, 480);
primaryStage.setScene(scene);
primaryStage.show();
}
private TreeTableView<Data> createTreeTableView() {
itemizedDataPool = EasyBind.map(filteredDataPool, DataItem::new);
TreeItem<Data> itemRoot = new TreeItem<>();
Bindings.bindContent(itemRoot.getChildren(), itemizedDataPool);
TreeTableView<Data> listView = new TreeTableView<>(itemRoot);
listView.setShowRoot(false);
itemRoot.setExpanded(true);
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
listView.getColumns().add(new TreeTableColumn<>("Data"));
return listView;
}
private Label createSummarizingLabel(ObservableList<TreeItem<Data>> selectedItems) {
Label sumLabel = new Label();
selectedData = EasyBind.map(selectedItems, (TreeItem<Data> t) -> ((DataItem) t).data);
selectedData.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
int sum = 0;
for (Data d : selectedData) {
sum += d.value;
}
sumLabel.setText("Sum: " + sum);
}
});
return sumLabel;
}
private Spinner<Integer> createLowerBoundFilter() {
Spinner<Integer> lowerBoundSelector = new Spinner<>(0, 20, 0, 1);
lowerBoundSelector.valueProperty().addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
filteredDataPool.setPredicate(t -> t.value > lowerBoundSelector.getValue());
}
});
return lowerBoundSelector;
}
public static void main(String[] args) {
launch(args);
}
}发布于 2016-03-04 11:38:55
问题
TreeTableView使用TreeTableViewArrayListSelectionModel,后者扩展了MultipleSelectionModelBase,后者使用ReadOnlyUnbackedObservableList,后者使用(并包含) SelectionListIterator,后者的方法nextIndex实现中断。
感谢fabian指出了这一点。他还提交了一份bug报告(id=8145887)。
解决办法
在中间使用缓冲区可以为上述问题提供有效的解决办法。我试过几种方法。选择失效的setAll和Bindings.bindContent 不工作。在这两种情况下,我在列表中都收到了null值。简单的“解决方案”是简单地过滤掉null。这会导致下面的代码效率低下,但显然是有效的。
// [...]
TreeTableView<Data> listView = createTreeTableView();
selectionBuffer = FXCollections.observableArrayList();
listView.getSelectionModel().getSelectedItems().addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
selectionBuffer.clear();
for (TreeItem<Data> t : listView.getSelectionModel().getSelectedItems()) {
if (t != null) {
selectionBuffer.add(t);
}
}
}
});
// [...]现在,使用selectionBuffer而不是listView.getSelectionModel().getSelectedItems()应该可以弥补nextIndex中的实现问题。
https://stackoverflow.com/questions/35778430
复制相似问题