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

JavaFX带复选框的TableView:获取复选框选中的行

JavaFX TableView复选框选中行获取问题修复

问题背景

我找了很久相关方案,但大多过时无效,现在遇到的问题是:TableView列头有全选复选框,每行也有独立复选框,需要获取所有复选框选中的行来执行后续操作,但当前代码里的获取方法都不生效。

现有控制器代码

package com.comparador.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.comparador.ComparadorPreciosApplication;
import com.comparador.entity.Commerce;
import com.comparador.entity.Items;
import com.comparador.entity.ShoppingListPrices;
import com.comparador.repository.CommerceRepository;
import com.comparador.repository.ProductRepository;
import com.comparador.service.ShoppingService;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;

@Component
public class ShoppingController implements Initializable {
//    @Autowired
//    @Qualifier("lblTitulo")
    private String titulo = "Productos";

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private CommerceRepository commerceRepository;
    @Autowired
    private ShoppingService shoppingService;

    @FXML
    private Label lblTitulo;
    @FXML
    private Button btBack;
    @FXML
    private TableView<Items> tvProducts;
    @FXML
    private TableColumn<Items, CheckBox> colSelected;       //THE CHECKBOX COLUMN
    @FXML
    private TableColumn<Items, String> colName;
    @FXML
    private TableColumn<Items, Integer> colAmount;
    @FXML
    private TableView<ShoppingListPrices> tvTotalPrices;
    @FXML
    private TableColumn<ShoppingListPrices, String> colCommerce;
    @FXML
    private TableColumn<ShoppingListPrices, Double> colTotal;

    private CheckBox selectAll;
    List<ShoppingListPrices> shoppingList = new ArrayList<>();
    
    
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        colName.setCellValueFactory(new PropertyValueFactory<>("name"));
        colAmount.setCellValueFactory(new PropertyValueFactory<>("amount"));
        colAmount.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
//      colSelected.setCellFactory(CheckBoxTableCell.forTableColumn(colSelected));
//      colSelected.setCellValueFactory(cellData -> new ReadOnlyBooleanWrapper(cellData.getValue().getChecked()));
        colSelected.setCellValueFactory(new PropertyValueFactory<>("selected"));
        colCommerce.setCellValueFactory(new PropertyValueFactory<>("commerceName"));
        colTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
        lblTitulo.setText(titulo);
        tvProducts.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        reloadTableViewProducts();
        
        selectAll = new CheckBox();
        selectAll.setOnAction(event -> {
            event.consume();
            tvProducts.getItems().forEach(item -> {
                item.getSelected().setSelected(selectAll.isSelected());
            });
            
        });
        
        setShoppingList();
        
        colSelected.setGraphic(selectAll);
    }
    
    @FXML
    public void editAmount(CellEditEvent<Items, Integer> event) {
        Items item = event.getRowValue();
        if(event.getTableColumn().getText().equals("Cantidad")) {
            item.setAmount(event.getNewValue());
        }
        setShoppingList();
    }
    
    /*
     * CLICKING ON A CHECKBOX SHOULD CALL THIS METHOD AND ADD THE ROW TO "selectedItems"
     */
    @FXML
    public void setShoppingList() {
        
        List<Items> selectedItems = new ArrayList<>();

        //Before trying this I was selecting each row by Ctrl + Clicking on it
//      List<Items> selectedItems = tvProducts.getSelectionModel().getSelectedItems();
        
        //This didn't seem to work
//      List<ShoppingListItems> selectedItems = tvProducts.getItems().filtered(x->x.getSelected() == true);
        
        
        List<Commerce> commerces = commerceRepository.findByNameContaining("");
        
        ShoppingListPrices pricesMixingCommerces = shoppingService.getCheapestShoppingList(commerces, selectedItems);
        List<ShoppingListPrices> pricesByCommerce = shoppingService.getShoppingListsPerCommerce(commerces, selectedItems);
        
        shoppingList = new ArrayList<>();
        shoppingList.add(pricesMixingCommerces); 
        shoppingList.addAll(pricesByCommerce);
        
        ObservableList<ShoppingListPrices> resultOL = FXCollections.observableArrayList();
        resultOL.addAll(shoppingList);
        tvTotalPrices.setItems(resultOL);
    }
    
    
    @FXML
    public void openShoppingList() throws IOException {
        FXMLLoader loader  = new FXMLLoader(getClass().getResource("/shoppingList.fxml"));
        ShoppingListController shoppingListController = new ShoppingListController();
        loader.setControllerFactory(ComparadorPreciosApplication.applicationContext::getBean);
        loader.setController(shoppingListController);
        shoppingListController.setup(tvTotalPrices.getSelectionModel().getSelectedItem());
        try {
            Scene scene = new Scene(loader.load(), 800, 400, true, SceneAntialiasing.BALANCED);
            Stage stage = new Stage();//(Stage) btBack.getScene().getWindow();
            stage.setUserData(tvTotalPrices.getSelectionModel().getSelectedItem());
            stage.setScene(scene);
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    
    @FXML
    public void goBack() {
        FXMLLoader loader  = new FXMLLoader(ComparadorPreciosApplication.class.getResource("/index.fxml"));
        loader.setControllerFactory(ComparadorPreciosApplication.applicationContext::getBean);
        try {
            Scene scene = new Scene(loader.load(), 800, 800, false, SceneAntialiasing.BALANCED);
            Stage stage = (Stage) btBack.getScene().getWindow();
            stage.setScene(scene);
            stage.show();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    
    private void reloadTableViewProducts() {
        List<String> productNames = productRepository.findOnProductPerName("");
        List<Items> items = new ArrayList<>();
        for(String name : productNames) {
            //items.add(new Items(new SimpleBooleanProperty(false), name, 1));
            Items item = new Items((CheckBox) new CheckBox(), name, 1);
            item.getSelected().setSelected(false);
            items.add(item);
        }
        ObservableList<Items> itemsOL = FXCollections.observableArrayList();
        itemsOL.addAll(items);
        tvProducts.setItems(itemsOL);
    }
}

问题分析与修复方案

1. 核心问题:Items类的复选框设计错误

当前在Items类中直接存储CheckBox对象,无法监听复选框状态变化,不符合JavaFX的响应式设计。正确做法是用BooleanProperty维护选中状态,自动触发UI更新和状态监听。

修改Items类:

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;

public class Items {
    private final BooleanProperty selected = new SimpleBooleanProperty(false);
    private final SimpleStringProperty name;
    private final SimpleIntegerProperty amount;

    public Items(String name, int amount) {
        this.name = new SimpleStringProperty(name);
        this.amount = new SimpleIntegerProperty(amount);
    }

    // 属性Getter/Setter
    public BooleanProperty selectedProperty() {
        return selected;
    }

    public boolean isSelected() {
        return selected.get();
    }

    public void setSelected(boolean selected) {
        this.selected.set(selected);
    }

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

    public SimpleStringProperty nameProperty() {
        return name;
    }

    public int getAmount() {
        return amount.get();
    }

    public void setAmount(int amount) {
        this.amount.set(amount);
    }

    public SimpleIntegerProperty amountProperty() {
        return amount;
    }
}

2. 修复TableColumn配置

在控制器initialize方法中,替换colSelected的配置,使用CheckBoxTableCell绑定到selected属性:

// 替换原colSelected配置
colSelected.setCellFactory(CheckBoxTableCell.forTableColumn(colSelected));
colSelected.setCellValueFactory(cellData -> cellData.getValue().selectedProperty());

3. 添加选中状态监听器

在reloadTableViewProducts方法中,给每个Items的selectedProperty添加监听器,触发setShoppingList更新:

private void reloadTableViewProducts() {
    List<String> productNames = productRepository.findOnProductPerName("");
    ObservableList<Items> itemsOL = FXCollections.observableArrayList();
    for(String name : productNames) {
        Items item = new Items(name, 1);
        // 监听选中状态变化,自动更新列表
        item.selectedProperty().addListener((obs, oldVal, newVal) -> setShoppingList());
        itemsOL.add(item);
    }
    tvProducts.setItems(itemsOL);
}

4. 完善全选复选框逻辑

更新全选事件处理,同时监听TableView列表变化,确保新行同步全选状态:

selectAll = new CheckBox();
selectAll.setOnAction(event -> {
    event.consume();
    tvProducts.getItems().forEach(item -> item.setSelected(selectAll.isSelected()));
});

// 监听列表变化,同步全选状态
tvProducts.getItems().addListener((ListChangeListener<Items>) c -> {
    boolean allSelected = tvProducts.getItems().stream().allMatch(Items::isSelected);
    selectAll.setSelected(allSelected);
});

// 监听单个项选中状态,更新全选框状态
tvProducts.getItems().forEach(item -> {
    item.selectedProperty().addListener((obs, oldVal, newVal) -> {
        boolean allSelected = tvProducts.getItems().stream().allMatch(Items::isSelected);
        selectAll.setSelected(allSelected);
    });
});

5. 修复选中项获取逻辑

修改setShoppingList中的选中项过滤代码:

@FXML
public void setShoppingList() {
    // 过滤所有选中的Items
    List<Items> selectedItems = tvProducts.getItems().filtered(Items::isSelected);
    
    List<Commerce> commerces = commerceRepository.findByNameContaining("");
    
    ShoppingListPrices pricesMixingCommerces = shoppingService.getCheapestShoppingList(commerces, selectedItems);
    List<ShoppingListPrices> pricesByCommerce = shoppingService.getShoppingListsPerCommerce(commerces, selectedItems);
    
    shoppingList = new ArrayList<>();
    shoppingList.add(pricesMixingCommerces); 
    shoppingList.addAll(pricesByCommerce);
    
    ObservableList<ShoppingListPrices> resultOL = FXCollections.observableArrayList(shoppingList);
    tvTotalPrices.setItems(resultOL);
}

修复后效果

  • 点击单行复选框,自动触发setShoppingList更新右侧表格
  • 全选复选框可同步所有行的选中状态
  • 正确获取所有选中行数据,传递给后续业务逻辑

内容的提问来源于stack exchange,提问作者Calfa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:25:31