我有一些自定义单元格的tableview。我已使用setCellSelectionEnabled(true)启用了单元格选择。现在我想强调选择单元格的整行。为了实现这一点,我使用PseudoClass使用以下2个链接的实现:
1. https://community.oracle.com/message/12308173#12308173
2. Get selected row from TableView
该解决方案适用于最初的几次点击,但只需点击几下即可冻结该行上的突出显示。然后选择单元格而不突出显示该行。
Screenshot 1 - Row is highlighted on selecting the cell - 所选择的细胞背景是深蓝色&行背景为浅蓝色
Screenshot 2 - Row is not highlighted on selecting the cell. Highlight on the row is freezed. - 在“附加数据”列中随机点击单元格后,该行上的突出显示将被冻结。仅突出显示所选单元格。
在调试时,我发现在行上的高亮显示被冻结后,Controller中的BooleanBinding(containsSelection.addListener)上的侦听器未被调用。我尝试过使用键盘和键盘几个单元格选择后发生同样的问题。有时需要10到12次点击才能触发&有一次,我花了大约75次点击来触发这个问题。
以下是代码:
控制器:
package sample;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.collections.*;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
public class RowHighlightController implements Initializable {
@FXML
private TableView<Person> personTableView;
@FXML
private TableColumn<Person, String> someDataColumn;
@FXML
private TableColumn<Person, String> someMoreDataColumn;
@FXML
private TableColumn<Person, String> additionalDataColumn;
Person person = new Person();
@Override
public void initialize(URL location, ResourceBundle resources) {
someDataColumn.setCellValueFactory(new PropertyValueFactory<>("someData"));
someMoreDataColumn.setCellValueFactory(new PropertyValueFactory<>("someMoreData"));
ObservableList<Person> personObservableList = FXCollections.observableArrayList();
additionalDataColumn.setCellValueFactory(new PropertyValueFactory<>("additionalData"));
someDataColumn.setCellFactory(e -> new EditableTextCell());
someMoreDataColumn.setCellFactory(e -> new EditableTextCell());
Person initPerson = setUpPersonData();
Person personData1 = null, personData2 = null, personData3 = null, personData4 = null, personData5 = null, personData6 = null;
try {
personData1 = (Person) initPerson.clone();
personData2 = (Person) initPerson.clone();
personData3 = (Person) initPerson.clone();
personData4 = (Person) initPerson.clone();
personData5 = (Person) initPerson.clone();
personData6 = (Person) initPerson.clone();
personData1.setSomeData("1");
personData1.setSomeMoreData("a");
personData2.setSomeData("2");
personData2.setSomeMoreData("b");
personData3.setSomeData("3");
personData3.setSomeMoreData("c");
personData4.setSomeData("4");
personData4.setSomeMoreData("d");
personData5.setSomeData("5");
personData5.setSomeMoreData("e");
personData6.setSomeData("6");
personData6.setSomeMoreData("f");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
personObservableList.addAll(personData1, personData2, personData3, personData4, personData5, personData6);
personTableView.setItems(personObservableList);
personTableView.getSelectionModel().setCellSelectionEnabled(true);
personTableView.getStylesheets().add(getClass().getResource("tableView.css").toExternalForm());
personTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
PseudoClass rowContainsSelectedCell = PseudoClass.getPseudoClass("contains-selection");
personTableView.getSelectionModel().getSelectedCells().addListener((ListChangeListener.Change<? extends TablePosition> c) -> {
rowsWithSelectedCells.clear();
Set<Integer> rows = personTableView.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
});
personTableView.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 ;
});
/* I have tried using below block of code to resolve the row highlight issue. But its behaves the same.
ObservableMap<Integer, Integer> selectedCellCountByRow = FXCollections.observableHashMap();
personTableView.getSelectionModel().getSelectedCells().addListener((ListChangeListener.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");
personTableView.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 ;
});*/
}
private Person setUpPersonData() {
try {
person.setSomeData("This is SomeData");
person.setSomeMoreData("This is SomeMoreDate");
person.setAdditionalData("This is AdditionalData");
} catch (Exception e1) {
e1.printStackTrace();
}
return person;
}
}
CSS:
.table-row-cell:contains-selection{
-fx-background-color:lightskyblue;
}
人类:
package sample;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty someData, someMoreData, additionalData;
public Person() {
this.someData = new SimpleStringProperty("");
this.someMoreData = new SimpleStringProperty("");
this.additionalData = new SimpleStringProperty("");
}
public Person(String someData, String someMoreData, String additionalData) {
this.someData = new SimpleStringProperty(someData);
this.someMoreData = new SimpleStringProperty(someMoreData);
this.additionalData = new SimpleStringProperty(additionalData);
}
@Override
public Object clone()throws CloneNotSupportedException{
String someDataCloned = this.someData.getValue();
String someMoreDataCloned = this.someMoreData.getValue();
String additionalDataCloned = this.additionalData.getValue();
Person personCloned = new Person(someDataCloned, someMoreDataCloned, additionalDataCloned);
return personCloned;
}
public String getSomeData() {
return someData.get();
}
public SimpleStringProperty someDataProperty() {
return someData;
}
public void setSomeData(String someData) {
this.someData.set(someData);
}
public String getSomeMoreData() {
return someMoreData.get();
}
public SimpleStringProperty someMoreDataProperty() {
return someMoreData;
}
public void setSomeMoreData(String someMoreData) {
this.someMoreData.set(someMoreData);
}
public String getAdditionalData() {
return additionalData.get();
}
public SimpleStringProperty additionalDataProperty() {
return additionalData;
}
public void setAdditionalData(String additionalData) {
this.additionalData.set(additionalData);
}
}
可编辑文字单元格:
package sample;
import javafx.beans.value.WritableValue;
import javafx.scene.control.*;
import java.util.Objects;
public class EditableTextCell<Person> extends TableCell<Person, String> {
private final TextField textField;
private boolean updating = false;
public EditableTextCell() {
textField = new TextField();
textField.textProperty().addListener((o, oldValue, newValue) -> {
if (!updating) {
((WritableValue<String>) getTableColumn().getCellObservableValue((Person) getTableRow().getItem())).setValue(newValue);
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(textField);
if (!Objects.equals(textField.getText(), item)) { // prevent own updates from moving the cursor
updating = true;
textField.setText(item);
updating = false;
}
}
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="1250.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.RowHighlightController">
<children>
<TableView fx:id="personTableView" layoutY="50.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="someDataColumn" prefWidth="84.0" text="Some Data" />
<TableColumn fx:id="someMoreDataColumn" minWidth="100.0" prefWidth="100.0" text="Some More Data" />
<TableColumn fx:id="additionalDataColumn" minWidth="150.0" prefWidth="150.0" text="Additional Data" />
</columns>
</TableView>
</children>
</AnchorPane>
EDIT1:
控制器:更新setRowFactory&amp;具有一些打印语句的监听器,用于监控值。
personTableView.getSelectionModel().getSelectedCells().addListener((
ListChangeListener.Change<? extends TablePosition> c) -> {
rowsWithSelectedCells.clear();
// Output for below statement is always - rowContainsSelectedCell in after rowsWithSelectedCells.clear() []
System.out.println("rowContainsSelectedCell in after rowsWithSelectedCells.clear() " + rowsWithSelectedCells);
Set<Integer> rows = personTableView.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
// Example Output for below statement is 'rowContainsSelectedCell in after rowsWithSelectedCells.addAll(rows) [3]'
// The number 3 is row number selected.
System.out.println("rowContainsSelectedCell in after rowsWithSelectedCells.addAll(rows) " + rowsWithSelectedCells);
});
personTableView.setRowFactory(tv -> {
TableRow<Person> row = new TableRow<>();
BooleanBinding containsSelection = Bindings.createBooleanBinding(
() -> {
// below 2 statements are not printed after highlighting on the row freezes
System.out.println("rowsWithSelectedCells in setRowFactory " + rowsWithSelectedCells);
System.out.println("row.indexProperty() in setRowFactory " + row.indexProperty().getValue());
return rowsWithSelectedCells.contains(row.getIndex());
}, rowsWithSelectedCells, row.indexProperty());
containsSelection.addListener((obs, didContainSelection, nowContainsSelection) ->
row.pseudoClassStateChanged(rowContainsSelectedCell, nowContainsSelection));
return row ;
});
答案 0 :(得分:0)
我遇到了同样的问题:使用普通的Java FX绑定框架,我无法使其运行超过几次单击。
但是,如果您使用ReactFX Val代替,它将可靠地工作。这是我的工作代码:
public static final PseudoClass CONTAINS_SELECTION = PseudoClass.getPseudoClass("contains-selection");
[...]
ObservableSet<Integer> rowsWithSelectedCells = FXCollections.observableSet();
tableView.getSelectionModel().getSelectedIndices().addListener((InvalidationListener) c -> {
rowsWithSelectedCells.clear();
Set<Integer> rows = tableView.getSelectionModel().getSelectedCells().stream().map(pos -> pos.getRow()).collect(Collectors.toSet());
rowsWithSelectedCells.addAll(rows);
});
tableView.setRowFactory(tv -> {
TableRow row = new TableRow<>();
Val<Boolean> containsSelectionVal = Val.create(() -> rowsWithSelectedCells.contains(row.getIndex()), rowsWithSelectedCells, row.indexProperty());
containsSelectionVal.addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(CONTAINS_SELECTION, newValue);
});
return row;
});