问题
In my TableView, I want to have a column that contains a checkbox. This checkbox is a way for the user to select which rows they want to be marked for a certain action, such as deletion. I have tried using a CheckBoxTableCell but all the examples I have seen have binded the checkbox's value to some boolean property of the model. The model on which my TableView is based on is a JPA entity and does not have such a field. I am also reluctant to add such a field because it would not be relevant to the model other than for this row selection purpose.
Therefore my question is, is it possible to use a CheckBoxTableCell for my purpose of selecting multiple rows. And if so, how can I implement it and afterwards get each row that was selected? If not, what is the best way to achieve multiple selection with a checkbox in a table column?
Thank you.
回答1:
First note you can achieve this functionality simply using the default selection implementation for the table view. I.e. call table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
. Then the user can select multiple rows and you can just operate on the selected rows.
If you want to use a check box column for this functionality, it's not too difficult:
CheckBoxTableCell
has a selectedStateCallback property, which is a function mapping the index of the cell (i.e. its row) to an ObservableValue<Boolean>
. The selected state of the check box will be bound (bidirectionally if the observable boolean is writable) to the state of the observable boolean which is the result of this function.
So, as you've seen, typically you map this to a BooleanProperty
in the model object, but you can map it to any property you like. If you don't have one, you can create one specifically for the purpose.
For example, you probably want to maintain some kind of collection of the items that are currently selected: for example you could use an ObservableSet
. (In the example below I am assuming there are no duplicate items, in the sense of .equals(...)
, in the table. If your table might contain duplicate rows, you need to do a bit more work.) Then you just register listeners to make sure that:
- If the property representing the selected state of the check box changes, you update the observable set of selected items
- If the observable set of selected items changes, you update the check box (by updating the boolean property)
- If the cell is reused to represent a different item, you update the check box. You can register a listener with the cell's
itemProperty
for this purpose.
Here's an example, using a very simple table displaying only strings. Because we want to get the item represented by the whole row from the cell's getItem
method, we make the type of the column the same as the type of the table (String
in this example, so we have TableColumn<String, String>
), and use a cellValueFactory
that wraps the object from the entire row: selectedCol.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
. In real life, you would have something more complex as your table type, and likely use a ReadOnlyObjectWrapper<>(cellData.getValue());
.
import java.util.HashSet;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener.Change;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableViewSelectByCheckBox extends Application {
@Override
public void start(Stage primaryStage) {
TableView<String> table = createTable();
TableColumn<String, String> selectedCol = new TableColumn<>();
selectedCol.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
ObservableSet<String> selectedItems = FXCollections.observableSet(new HashSet<>());
selectedCol.setCellFactory(col -> {
// boolean property represents if the check box in the cell is selected:
BooleanProperty selected = new SimpleBooleanProperty();
// create check box cell and bind its state to property:
CheckBoxTableCell<String, String> cell = new CheckBoxTableCell<>(index -> selected);
// update set of selected indices if checkbox state changes:
selected.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
selectedItems.add(cell.getItem());
} else {
selectedItems.remove(cell.getItem());
}
});
// update check box state if set of selected indices changes:
selectedItems.addListener((Change<? extends String> change) -> {
selected.set(cell.getItem() != null && selectedItems.contains(cell.getItem()));
});
// update check box when cell is reused for a different index:
cell.itemProperty().addListener((obs, oldItem, newItem) -> {
selected.set(newItem != null && selectedItems.contains(newItem));
});
return cell ;
});
table.getColumns().add(0, selectedCol);
Button button = new Button("Show selected");
button.setOnAction(e -> selectedItems.forEach(System.out::println));
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(5));
BorderPane root = new BorderPane(table, null, null, button, null);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private TableView<String> createTable() {
TableView<String> table = new TableView<>();
table.setEditable(true);
TableColumn<String, String> itemCol = new TableColumn<>("Item");
itemCol.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(itemCol);
for (int item = 1 ; item <= 100; item++) {
table.getItems().add("Item "+item);
}
return table ;
}
public static void main(String[] args) {
launch(args);
}
}
来源:https://stackoverflow.com/questions/34940563/can-i-use-a-checkboxtablecell-in-my-tableview-as-a-way-to-let-the-user-select-mu