You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX MVC模式下多触发源ListView动态过滤实现问题

实现方案

逻辑分工说明

  • 所有视图绑定、数据过滤、剪贴板监听逻辑全部放在Controller层,符合MVC的职责划分,Main类仅作为程序启动入口,不处理任何视图相关逻辑
  • 数据流转逻辑:
    1. 维护一个原始的ObservableList存储所有剪贴板历史内容
    2. 用FilteredList包装原始列表作为ListView的直接数据源,过滤逻辑仅修改FilteredList的Predicate即可,不需要改动原始数据
    3. 搜索框输入变化时动态更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 11:45:03