JavaFX MVC模式下多触发源ListView动态过滤实现问题
实现方案
逻辑分工说明
- 所有视图绑定、数据过滤、剪贴板监听逻辑全部放在Controller层,符合MVC的职责划分,Main类仅作为程序启动入口,不处理任何视图相关逻辑
- 数据流转逻辑:
- 维护一个原始的
ObservableList存储所有剪贴板历史内容 - 用
FilteredList包装原始列表作为ListView的直接数据源,过滤逻辑仅修改FilteredList的Predicate即可,不需要改动原始数据 - 搜索框输入变化时动态更新
Predicate实现实时过滤
- 维护一个原始的
代码修改
1. 简化Main类
仅保留启动逻辑,移除剪贴板监听等视图相关代码:
import Controller.Controller01; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.geometry.Rectangle2D; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Screen; import javafx.stage.Stage; public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { FXMLLoader loader =new FXMLLoader(); loader.setLocation(Main.class.getResource("/View/View01.fxml")); Parent root = loader.load(); Scene scene = new Scene(root); primaryStage.setScene(scene); Rectangle2D bounds = Screen.getPrimary().getVisualBounds(); double x = bounds.getMinX() + (bounds.getWidth() - scene.getWidth()) * 0.2; double y = bounds.getMinY() + (bounds.getHeight() - scene.getHeight()) * 0.55; primaryStage.setX(x); primaryStage.setY(y); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
2. 修改Controller类
调整数据绑定逻辑,统一处理剪贴板监听和搜索过滤:
package Controller; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; import java.net.URL; import java.util.ResourceBundle; public class Controller01 implements Initializable { @FXML private Button myButton01; @FXML public ListView<String> mylistviewvar; @FXML private ToggleButton myToggleBut; @FXML private AnchorPane myAnchorPane; @FXML private TextField searchFieldvar; // 原始数据列表:存储所有剪贴板历史 private final ObservableList<String> originalData = FXCollections.observableArrayList(); // 过滤后的列表:作为ListView的直接数据源 private FilteredList<String> filteredData; @FXML void myButton01myEventClicked(MouseEvent event) { System.out.println("hi there !"); Clipboard clipboard = Clipboard.getSystemClipboard(); ClipboardContent content = new ClipboardContent(); content.putString("Some text"); clipboard.setContent(content); String fromclipboardstr = Clipboard.getSystemClipboard().getString(); System.out.println(fromclipboardstr); } @FXML void myToggleButEventSB(MouseEvent event) { Stage stage = (Stage) myAnchorPane.getScene().getWindow(); stage.setAlwaysOnTop(myToggleBut.isSelected()); System.out.println(myToggleBut.isSelected() ? "is ON" : "is OFF"); } @Override public void initialize(URL url, ResourceBundle resourceBundle) { // 初始化过滤列表,默认不过滤 filteredData = new FilteredList<>(originalData, s -> true); // 绑定过滤列表到ListView mylistviewvar.setItems(filteredData); // 搜索框输入监听 searchFieldvar.textProperty().addListener((obs, oldVal, newVal) -> { if (newVal == null || newVal.trim().isEmpty()) { filteredData.setPredicate(s -> true); } else { filteredData.setPredicate(s -> s.contains(newVal.trim())); } }); // 剪贴板监听 final Clipboard systemClipboard = Clipboard.getSystemClipboard(); new com.sun.glass.ui.ClipboardAssistance(com.sun.glass.ui.Clipboard.SYSTEM) { @Override public void contentChanged() { if (systemClipboard.hasString()) { String content = systemClipboard.getString().trim(); // 避免重复添加相同内容 if (originalData.isEmpty() || !originalData.get(originalData.size()-1).equals(content)) { // 剪贴板回调不在FX线程,更新UI要包Platform.runLater Platform.runLater(() -> originalData.add(content)); } } } }; } }
注意点
- 不要直接调用
mylistviewvar.getItems().add()添加内容,所有新增内容都加到originalData原始列表,FilteredList会自动同步变更 - 剪贴板的
contentChanged回调运行在系统线程,所有JavaFX UI相关操作必须包裹在Platform.runLater()中,否则会抛出线程异常 FilteredList是原始列表的视图包装,不会修改原始数据,过滤只是控制哪些内容显示,不会丢失历史记录
内容的提问来源于stack exchange,提问作者progLearn00
相关产品推荐
相关产品推荐

