JavaFX8添加FilteredList和SortedList实现搜索栏触发InvocationTargetException
先看你提供的堆栈跟踪,最关键的错误是这行:
Caused by: java.lang.NullPointerException at javafx.collections.transformation.TransformationList.
(TransformationList.java:65)
这说明你在创建FilteredList的时候,传入的源列表是null,根本没完成初始化。
问题出在哪?
你只声明了private ObservableList<DataHandler_ExamsListing> EListingData;但没给它赋值,而且应该是在控制器的构造函数里就尝试创建FilteredList了——这时候LoadEListingData()还没执行,EListingData还是个空引用,自然会抛出空指针。
怎么修复?
这里给你两个可行的方案:
方案1:提前初始化ObservableList
直接在声明的时候就初始化列表,哪怕还没加载数据,它也是个空列表而不是null:
private ObservableList<DataHandler_ExamsListing> EListingData = FXCollections.observableArrayList();
这样你之后创建FilteredList时,就不会因为源列表为null报错了。
方案2:调整FilteredList的创建时机
别在构造函数里碰FilteredList,把初始化逻辑移到initialize()方法里——这是JavaFX控制器的标准初始化方法,会在FXML加载完成后自动执行,而且你可以确保数据加载完成后再创建过滤和排序列表。
示例代码参考:
private ObservableList<DataHandler_ExamsListing> EListingData; private FilteredList<DataHandler_ExamsListing> filteredData; private SortedList<DataHandler_ExamsListing> sortedData; @Override public void initialize(URL url, ResourceBundle rb) { // 先加载数据库数据 LoadEListingData(); // 初始化过滤列表,默认显示所有数据 filteredData = new FilteredList<>(EListingData, p -> true); // 给搜索文本框添加监听,实现实时过滤 searchTextField.textProperty().addListener((observable, oldValue, newValue) -> { filteredData.setPredicate(data -> { // 搜索框为空时显示全部 if (newValue == null || newValue.trim().isEmpty()) { return true; } // 按你的需求编写匹配逻辑,这里示例匹配ID、考试名、日期 String filter = newValue.toLowerCase().trim(); return String.valueOf(data.getExamID()).contains(filter) || data.getExamName().toLowerCase().contains(filter) || data.getExamDate().toLowerCase().contains(filter); }); }); // 初始化排序列表,绑定到TableView的排序规则 sortedData = new SortedList<>(filteredData); sortedData.comparatorProperty().bind(yourTableView.comparatorProperty()); // 把排序后的数据设置给TableView yourTableView.setItems(sortedData); }
另外,你提供的LoadEListingData()代码不完整,记得要完整读取ResultSet的数据,最好用try-with-resources来自动关闭数据库资源,避免泄漏:
private void LoadEListingData(){ EListingData = FXCollections.observableArrayList(); // try-with-resources自动关闭连接、statement、resultSet try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM Exam")) { while (rs.next()) { EListingData.add(new DataHandler_ExamsListing( rs.getInt("ExamID"), rs.getString("ExamName"), rs.getString("ExamDate") // 这里补充你需要的其他字段 )); } } catch (SQLException e) { e.printStackTrace(); // 这里可以添加错误提示,比如弹出Alert } }
小提醒
永远别在控制器的构造函数里操作FXML组件或者依赖未初始化的数据——构造函数执行时,FXML里的控件还没完成加载,你的数据列表也大概率还没准备好,initialize()方法才是做初始化工作的正确地方。
内容的提问来源于stack exchange,提问作者Zaryab Waseem

