如何在双ListView选择系统中实现筛选并保持列表可编辑?
解决ListView筛选时条目移动的UnsupportedOperationException问题
我来帮你搞定这个问题!你遇到的UnsupportedOperationException主要有两个核心原因:
- 错误地用
FXCollections.observableList()包装了SortedList,这个方法返回的是不可修改的列表包装对象 - 直接操作了ListView的过滤视图(
lvAvailable.getItems()),而不是原始的数据源列表
核心原理
FilteredList和SortedList都是基于原始ObservableList的视图层,它们本身不存储数据,只是对原始数据进行过滤/排序后的展示载体。所有的数据增删操作都应该直接针对原始的availableList和selectedList,这样视图会自动同步更新,同时保证列表的可编辑性。
修改后的控制器代码
下面是修复后的完整Controller类,我标注了关键修改点:
public class Controller { @FXML private TextField txtSearch; @FXML private ListView<String> lvAvailable; @FXML private ListView<String> lvSelected; private ObservableList<String> availableList = FXCollections.observableArrayList(); private ObservableList<String> selectedList = FXCollections.observableArrayList(); @FXML private void initialize() { // 初始化可用列表数据 availableList.addAll( "One", "Two", "Three", "Four", "Five" ); // 1. 创建过滤列表,基于原始的availableList FilteredList<String> filteredList = new FilteredList<>(availableList); // 2. 创建排序列表,基于过滤列表 SortedList<String> sortedList = new SortedList<>(filteredList); // 3. 直接把SortedList设置给ListView,不需要额外包装 lvAvailable.setItems(sortedList); lvAvailable.setPlaceholder(new Label("No results")); // 搜索过滤逻辑保持不变 txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { filteredList.setPredicate(availableItem -> { if (newValue == null || newValue.isEmpty()) { return true; } String query = newValue.toLowerCase(); return availableItem.toLowerCase().contains(query); }); }); // 双击移动条目逻辑 - 修改为操作原始列表 lvAvailable.setOnMouseClicked(mouseEvent -> { if (mouseEvent.getButton().equals(MouseButton.PRIMARY) && mouseEvent.getClickCount() == 2) { String selectedItem = lvAvailable.getSelectionModel().getSelectedItem(); addItem(selectedItem); } }); lvSelected.setOnMouseClicked(mouseEvent -> { if (mouseEvent.getButton().equals(MouseButton.PRIMARY) && mouseEvent.getClickCount() == 2) { String selectedItem = lvSelected.getSelectionModel().getSelectedItem(); removeItem(selectedItem); } }); // 把selectedList直接设置给已选ListView lvSelected.setItems(selectedList); } private void addItem(String item) { if (item != null && !selectedList.contains(item)) { // 操作原始列表:从可用列表移除,添加到已选列表 availableList.remove(item); selectedList.add(item); } } private void removeItem(String item) { if (item != null && !availableList.contains(item)) { // 操作原始列表:从已选列表移除,添加到可用列表 selectedList.remove(item); availableList.add(item); } } }
关键修改点说明
- 移除不必要的列表包装:
- 删掉了
FXCollections.observableList(sortedList),直接用lvAvailable.setItems(sortedList),因为SortedList本身就是ObservableList的实现类,不需要额外包装。
- 删掉了
- 操作原始数据源而非视图:
addItem和removeItem方法中,不再操作lvAvailable.getItems()或lvSelected.getItems(),而是直接修改availableList和selectedList。这样既避免了不可修改列表的异常,又能让过滤/排序视图自动同步更新。
- 直接绑定已选列表:
- 把
selectedList直接设置给lvSelected.setItems(selectedList),简化代码逻辑,同时保证已选列表的可编辑性。
- 把
这样修改后,你不仅能正常实现双击移动条目的功能,搜索筛选也能正常工作,而且不会再出现UnsupportedOperationException了。
内容的提问来源于stack exchange,提问作者Zephyr
相关产品推荐
相关产品推荐

