首页 > Java > java教程 > 正文

JavaFX TableView 动态过滤与选中项获取及跨视图数据传递教程

霞舞
发布: 2025-11-07 13:46:12
原创
370人浏览过

JavaFX TableView 动态过滤与选中项获取及跨视图数据传递教程

本教程详细阐述了如何在 javafx 中实现 tableview 的动态数据过滤功能,通过 textfield 实时更新显示内容。重点讲解了如何在用户点击按钮时,从已过滤的 tableview 中准确获取当前显示的数据(特别是第一个匹配项),并演示了如何将这些获取到的数据安全地传递给另一个 fxml 视图及其控制器,以实现复杂的应用交互逻辑。

JavaFX TableView 高级用法:实现动态过滤与选中项获取

在 JavaFX 应用开发中,TableView 是一个常用的组件,用于展示结构化数据。当数据量较大时,用户往往需要通过搜索或过滤功能来快速定位所需信息。本教程将指导您如何实现一个带有动态过滤功能的 TableView,并在用户点击按钮时,获取当前过滤结果中的数据,并将其传递给另一个 FXML 视图。

1. 核心组件概览

为了实现动态过滤和数据获取,我们将使用以下 JavaFX 组件和集合类:

  • TableView: 显示数据的表格。
  • TableColumn: TableView 中的列定义。
  • TextField: 用户输入过滤关键词的文本框。
  • Button: 触发数据获取和视图切换的按钮。
  • ObservableList: 存储原始数据,支持数据变更通知。
  • FilteredList: 基于 ObservableList 创建,通过 Predicate 实现数据过滤。
  • SortedList: 基于 FilteredList 创建,支持数据排序,并最终绑定到 TableView。

2. 构建数据模型

首先,我们需要一个简单的数据模型来表示表格中的每一行。以 Employee 为例:

// Employee.java
public class Employee {
    private final SimpleIntegerProperty id;
    private final SimpleStringProperty name;
    private final SimpleStringProperty salary;

    public Employee(int id, String name, String salary) {
        this.id = new SimpleIntegerProperty(id);
        this.name = new SimpleStringProperty(name);
        this.salary = new SimpleStringProperty(salary);
    }

    public int getId() {
        return id.get();
    }

    public SimpleIntegerProperty idProperty() {
        return id;
    }

    public void setId(int id) {
        this.id.set(id);
    }

    public String getName() {
        return name.get();
    }

    public SimpleStringProperty nameProperty() {
        return name;
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public String getSalary() {
        return salary.get();
    }

    public SimpleStringProperty salaryProperty() {
        return salary;
    }

    public void setSalary(String salary) {
        this.salary.set(salary);
    }
}
登录后复制

3. FXML 布局配置

在 FXML 文件中,我们需要定义 TableView、TextField 和 Button。确保为它们设置 fx:id 和 onAction 属性,以便在控制器中引用和处理事件。

立即学习Java免费学习笔记(深入)”;

千面视频动捕
千面视频动捕

千面视频动捕是一个AI视频动捕解决方案,专注于将视频中的人体关节二维信息转化为三维模型动作。

千面视频动捕 27
查看详情 千面视频动捕
<!-- YourView.fxml -->
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.GridPane?>

<AnchorPane prefHeight="600.0" prefWidth="950.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="yourpackage.YourController">
   <children>
      <GridPane layoutX="14.0" layoutY="14.0" prefHeight="572.0" prefWidth="929.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints maxHeight="100.0" minHeight="10.0" prefHeight="50.0" vgrow="SOMETIMES" />
          <RowConstraints maxHeight="500.0" minHeight="10.0" prefHeight="500.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <TableView fx:id="mTableView" prefHeight="451.0" prefWidth="929.0" GridPane.rowIndex="1">
               <columns>
                  <TableColumn fx:id="idColumn" prefWidth="75.0" text="ID" />
                  <TableColumn fx:id="nameColumn" prefWidth="75.0" text="Name" />
                  <TableColumn fx:id="salColumn" prefWidth="75.0" text="Salary" />
               </columns>
               <columnResizePolicy>
                  <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
               </columnResizePolicy>
            </TableView>
            <AnchorPane prefHeight="200.0" prefWidth="200.0">
               <children>
                  <TextField fx:id="mTextField" layoutX="14.0" layoutY="11.0" prefHeight="28.0" prefWidth="366.0" promptText="Enter ID to search" />
                  <Button fx:id="searchBtn" layoutX="394.0" layoutY="12.0" mnemonicParsing="false" onAction="#handleSearch" prefHeight="26.0" prefWidth="166.0" text="Search" />
               </children>
            </AnchorPane>
         </children>
      </GridPane>
   </children>
</AnchorPane>
登录后复制

4. 控制器逻辑实现

控制器是实现所有业务逻辑的地方。我们将在这里初始化 TableView、绑定数据、实现过滤逻辑以及处理按钮点击事件

// YourController.java
package yourpackage;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class YourController implements Initializable {

    @FXML
    private TableView<Employee> mTableView;
    @FXML
    private TableColumn<Employee, Integer> idColumn;
    @FXML
    private TableColumn<Employee, String> nameColumn;
    @FXML
    private TableColumn<Employee, String> salColumn;
    @FXML
    private TextField mTextField;
    @FXML
    private Button searchBtn;

    private ObservableList<Employee> mList = FXCollections.observableArrayList();
    private FilteredList<Employee> filteredList;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        // 1. 初始化 TableColumn 的单元格值工厂
        idColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        salColumn.setCellValueFactory(new PropertyValueFactory<>("salary"));

        // 2. 加载数据 (这里使用模拟数据,实际应用中可能从数据库加载)
        loadRecords();

        // 3. 创建 FilteredList 并绑定到原始数据
        filteredList = new FilteredList<>(mList, b -> true); // b->true 表示默认显示所有数据

        // 4. 为 TextField 添加监听器,实现动态过滤
        mTextField.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredList.setPredicate(employee -> {
                // 如果搜索框为空或空白,则显示所有数据
                if (newValue == null || newValue.isEmpty() || newValue.isBlank()) {
                    return true;
                }

                String searchKeyword = newValue.toLowerCase();

                // 根据 ID 进行过滤 (这里只过滤ID,您可以扩展到其他字段)
                return String.valueOf(employee.getId()).toLowerCase().contains(searchKeyword);
            });
        });

        // 5. 将 FilteredList 包装成 SortedList
        SortedList<Employee> sortedList = new SortedList<>(filteredList);

        // 6. 将 SortedList 的比较器属性绑定到 TableView 的比较器属性
        // 这样 TableView 的排序操作会影响 SortedList
        sortedList.comparatorProperty().bind(mTableView.comparatorProperty());

        // 7. 将 SortedList 设置为 TableView 的数据源
        mTableView.setItems(sortedList);
    }

    /**
     * 模拟加载数据到 ObservableList
     * 实际应用中会从数据库或其他数据源获取
     */
    private void loadRecords() {
        mList.add(new Employee(101, "Alice", "50000"));
        mList.add(new Employee(102, "Bob", "60000"));
        mList.add(new Employee(103, "Charlie", "75000"));
        mList.add(new Employee(201, "David", "55000"));
        mList.add(new Employee(202, "Eve", "62000"));
        mList.add(new Employee(301, "Frank", "80000"));
    }

    /**
     * 处理搜索按钮点击事件
     * 获取当前 TableView 中过滤后的数据,并传递给另一个 FXML 视图
     */
    @FXML
    public void handleSearch(ActionEvent event) {
        // 获取 TableView 当前显示的所有项 (这些项已经是经过过滤和排序的)
        ObservableList<Employee> currentDisplayedItems = mTableView.getItems();

        if (!currentDisplayedItems.isEmpty()) {
            // 获取第一个匹配的员工对象
            Employee selectedEmployee = currentDisplayedItems.get(0);
            System.out.println("Selected Employee (first filtered): " + selectedEmployee.getName() + " (ID: " + selectedEmployee.getId() + ")");

            // 示例:加载另一个 FXML 视图并传递数据
            try {
                // 假设您有一个名为 "DetailView.fxml" 的新视图
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/yourpackage/DetailView.fxml"));
                Parent root = loader.load();

                // 获取新视图的控制器
                DetailController detailController = loader.getController();
                // 调用新控制器的方法来传递数据
                detailController.setEmployeeDetails(selectedEmployee);

                // 创建并显示新场景
                Stage stage = new Stage();
                stage.setTitle("Employee Details");
                stage.setScene(new Scene(root));
                stage.show();

                // 可选:关闭当前窗口
                // ((Stage) searchBtn.getScene().getWindow()).close();

            } catch (IOException e) {
                e.printStackTrace();
                // 处理加载 FXML 失败的错误
            }
        } else {
            System.out.println("No employee found matching the filter criteria.");
            // 可以在此处显示一个警告框通知用户
        }
    }
}
登录后复制

5. 创建目标 FXML 视图及控制器

为了演示数据传递,我们需要一个接收数据的目标 FXML 视图和其对应的控制器。

<!-- DetailView.fxml -->
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox alignment="CENTER" prefHeight="200.0" prefWidth="300.0" spacing="10.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="yourpackage.DetailController">
   <children>
      <Label text="Employee Details">
         <font>
            <Font size="20.0" />
         </font>
      </Label>
      <Label fx:id="idLabel" text="ID: " />
      <Label fx:id="nameLabel" text="Name: " />
      <Label fx:id="salaryLabel" text="Salary: " />
   </children>
</VBox>
登录后复制
// DetailController.java
package yourpackage;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class DetailController {

    @FXML
    private Label idLabel;
    @FXML
    private Label nameLabel;
    @FXML
    private Label salaryLabel;

    /**
     * 设置员工详细信息到 UI 控件
     * @param employee 接收到的员工对象
     */
    public void setEmployeeDetails(Employee employee) {
        if (employee != null) {
            idLabel.setText("ID: " + employee.getId());
            nameLabel.setText("Name: " + employee.getName());
            salaryLabel.setText("Salary: " + employee.getSalary());
        }
    }
}
登录后复制

6. 注意事项与最佳实践

  • 获取过滤后的数据: 关键在于 mTableView.getItems() 方法。它返回的是 TableView 当前实际显示的数据(已经过 FilteredList 和 SortedList 处理)。直接访问 mList 或 filteredList 将无法反映 TableView 当前的显示状态。
  • 处理空结果集: 在 handleSearch 方法中,务必检查 currentDisplayedItems.isEmpty()。如果 TableView 中没有匹配项,尝试 get(0) 会导致 IndexOutOfBoundsException。
  • 处理多结果集: 当前示例只获取了第一个匹配项 (get(0))。如果需要处理所有匹配项,您可以遍历 currentDisplayedItems 列表。如果业务逻辑要求用户从多个匹配项中选择一个,您可能需要提供一个选择界面,或者修改 TableView 的选择模型。
  • 性能优化: 对于非常大的数据集,过滤操作可能会影响性能。考虑使用更高效的数据结构或在后台线程中执行复杂的过滤逻辑。
  • 错误处理: 在加载 FXML 文件时,务必捕获 IOException,并提供友好的错误提示。
  • 代码模块化: 将数据加载、过滤逻辑等拆分成独立的私有方法,提高代码的可读性和可维护性。
  • PropertyValueFactory 参数: PropertyValueFactory 构造函数中的字符串参数必须与数据模型 (Employee 类) 中属性的 getter 方法名匹配(例如,"id" 对应 getId() 和 idProperty())。

总结

通过本教程,您应该已经掌握了在 JavaFX 中实现 TableView 动态过滤的核心技术,包括使用 FilteredList 和 SortedList,以及如何监听 TextField 的输入变化。更重要的是,您学会了如何在按钮点击事件中准确获取 TableView 当前显示的数据,并将其作为参数传递给另一个 FXML 视图的控制器,从而实现复杂的界面交互和数据流转。这些技术是构建功能丰富的 JavaFX 应用的基础。

以上就是JavaFX TableView 动态过滤与选中项获取及跨视图数据传递教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号