我的TableView
配置如下:
tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
我可以通过调用
来获取所选单元格tableView.getSelectionModel().getSelectedCells()
我可以通过调用
来获取所选项目tableView.getSelectionModel().getSelectedItems()
不幸的是,似乎没有像上面那样的方法来获取所选行..
我想要获得的是一个配置为选择单元格的表,但无论如何突出显示相应的行。
答案 0 :(得分:1)
选择模型中没有API可以为您提供实际的TableRow
。这是有道理的,因为模型不应该知道正在观察它的任何UI元素。
这里的步骤有点棘手。您需要创建某种可观察的集合,以跟踪哪些行包含选定的单元格。这是一个使用可观察集的相当天真的实现:
ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
rowsWithSelectedCells.clear();
Set<Integer> rows = table.getSelectionModel().getSelectedCells().stream()
.map(pos -> pos.getRow())
.collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
});
现在让你的表行观察这个集合,并相应地更新它们的样式。为此,请在表格中使用rowFactory
:
PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
table.setRowFactory(tv -> {
TableRow<Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() -> rowsWithSelectedCells.contains(row.getIndex()), rowsWithSelectedCells, row.indexProperty());
containsSelection.addListener((obs, didContainSelection, nowContainsSelection) ->
row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
return row ;
});
这会通过在行上设置CSS伪类来更新样式。在外部样式表中,您可以执行类似
的操作.table-row-cell:contains-selection {
-fx-background: yellow ;
}
突出显示这些行。
这是一个SSCCE。文件style.css
仅包含上述CSS:
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableSet;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class TableHighlightRowsWithSelectedCells extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.getSelectionModel().setCellSelectionEnabled(true);
ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
rowsWithSelectedCells.clear();
Set<Integer> rows = table.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
});
PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
table.setRowFactory(tv -> {
TableRow<Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() -> rowsWithSelectedCells.contains(row.getIndex()), rowsWithSelectedCells, row.indexProperty());
containsSelection.addListener((obs, didContainSelection, nowContainsSelection) ->
row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
return row ;
});
table.getColumns().add(column("First Name", Person::firstNameProperty));
table.getColumns().add(column("Last Name", Person::lastNameProperty));
table.getColumns().add(column("Email", Person::emailProperty));
table.getItems().addAll(
new Person("Jacob", "Smith", "jacob.smith@example.com"),
new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
new Person("Ethan", "Williams", "ethan.williams@example.com"),
new Person("Emma", "Jones", "emma.jones@example.com"),
new Person("Michael", "Brown", "michael.brown@example.com")
);
Scene scene = new Scene(table, 600, 600);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
public static class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}
最后,一个快速的表现说明。每次选择更改时,将完全重建包含具有所选单元格的行的索引的可观察集。这可能很好,但是对于可能选择大量单元格的非常大的表,性能可能是个问题。性能稍好的实现是跟踪每行选择了多少个单元格,并递增或递减它:
ObservableMap<Integer, Integer> selectedCellCountByRow = FXCollections.observableHashMap();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> c) -> {
while (c.next()) {
if (c.wasAdded()) {
for (TablePosition<?,?> p : c.getAddedSubList()) {
int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(), 0);
int newCount = currentCount + 1 ;
selectedCellCountByRow.put(new Integer(p.getRow()), newCount);
System.out.println("Count now: "+selectedCellCountByRow.get(p.getRow()));
}
}
if (c.wasRemoved()) {
for (TablePosition<?, ?> p : c.getRemoved()) {
int currentCount = selectedCellCountByRow.getOrDefault(p.getRow(), 0);
int newCount = currentCount - 1 ;
if (newCount <= 0) {
selectedCellCountByRow.remove(p.getRow());
} else {
selectedCellCountByRow.put(p.getRow(), newCount);
}
}
}
}
});
然后行工厂将更新如下:
PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
table.setRowFactory(tv -> {
TableRow<Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() -> selectedCellCountByRow.containsKey(row.getIndex()), selectedCellCountByRow, row.indexProperty());
containsSelection.addListener((obs, didContainSelection, nowContainsSelection) ->
row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
return row ;
});