在可编辑的 ListView 中使用 filteredList

Using filteredList in an editable ListView

我正在为我的 ListView 使用 FilteredList 来启用搜索。问题是 FilteredList 不允许以任何方式改变数据,它只响应底层 ObservableList 的变化。

此外,它被声明为 final,所以我不能简单地扩展它以将编辑请求转发给源。

那么,如何在可编辑的 ListView 中使用它?

这是重现问题的代码

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        //problematic code
        var observableList = FXCollections.observableArrayList("name", "name 2", "name 3");
        FilteredList<String> filteredList = new FilteredList<>(observableList);
        
        var list = new ListView<>(filteredList);
        list.setEditable(true);
        list.setCellFactory(TextFieldListCell.forListView());

        //boilerplate code
        VBox wrapper = new VBox(list);
        primaryStage.setScene(new Scene(wrapper));
        primaryStage.show();
    }
}

编辑:添加了一个最小的可重现示例

正如您所注意到的,问题是 TransformationList (Sorted/FilteredList) 的具体实现中的 none 是可修改的。因此,默认提交处理程序在尝试设置 newValue:

时失败(带有 UnsupportedOperationException)
private EventHandler<ListView.EditEvent<T>> DEFAULT_EDIT_COMMIT_HANDLER = t -> {
    int index = t.getIndex();
    List<T> list = getItems();
    if (index < 0 || index >= list.size()) return;
    list.set(index, t.getNewValue());
};

出路是自定义提交处理程序。它的实现取决于上下文,它可以

  • 在基础源列表中设置一个新项目
  • 修改一个属性项

设置项目的代码片段:

// monolithic items
ObservableList<String> data = FXCollections.observableArrayList("afirst", "abString", "other");
FilteredList<String> filteredData = new FilteredList<>(data);
filteredData.setPredicate(text -> text.contains("a"));

// set up an editable listView
ListView<String> list = new ListView<>(filteredData);
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());

// commitHandler resetting the underlying data element
list.setOnEditCommit(v -> {
    ObservableList<String> items = list.getItems();
    int index = v.getIndex();
    if (items instanceof TransformationList<?, ?>) {
        TransformationList transformed = (TransformationList) items;
        items = transformed.getSource();
        index = transformed.getSourceIndex(index);
    }
    items.set(index, v.getNewValue());
});

更改项目 属性 的代码段:

// items with properties
ObservableList<MenuItem> data = FXCollections.observableArrayList(
        new MenuItem("afirst"), new MenuItem("abString"), new MenuItem("other"));
FilteredList<MenuItem> filteredData = new FilteredList<>(data);
// filter on text property
filteredData.setPredicate(menuItem -> menuItem.getText().contains("a"));

// set up an editable listView
ListView<MenuItem> list = new ListView<>(filteredData);
list.setEditable(true);
// converter for use in TextFieldListCell
StringConverter<MenuItem> converter = new StringConverter<>() {

    @Override
    public String toString(MenuItem menuItem) {
        return menuItem != null ? menuItem.getText() : null;
    }

    @Override
    public MenuItem fromString(String text) {
        return new MenuItem(text);
    }
    
};
list.setCellFactory(TextFieldListCell.forListView(converter));

// commitHandler changing a property of the item
list.setOnEditCommit(v -> {
    ObservableList<MenuItem> items = list.getItems();
    MenuItem column = items.get(v.getIndex());
    MenuItem standIn = v.getNewValue();
    column.setText(standIn.getText());
});