Issue
I am making an App that has a TableView. This TableView has 4 columns, of which the first column uses a CheckBox. The data displayed in the TableView is from a Bill class with name, dateDue and Amount making up the other 3 columns. When the CheckBox is selected (which is the default on start up) all the bills in the Table are displayed in white colour. When a CheckBox is unselected, the bill on that row needs to be 'ghosted' or change the colour of the characters.
In my fxml file I have used the code:
<TableColumn prefWidth="49.0" text="On/Off" fx:id="checkBoxSelectionColumn">
<cellValueFactory>
<BillsCheckBox/>
</cellValueFactory>
</TableColumn>
The associated BillCheckBox class:
public class BillsCheckBox implements Callback<TableColumn.CellDataFeatures<Bill, CheckBox>, ObservableValue<CheckBox>> {
@Override
public ObservableValue<CheckBox> call(TableColumn.CellDataFeatures<Bill, CheckBox> param) {
Bill bill = param.getValue();
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().setValue(true);
checkBox.selectedProperty().addListener((ov, old_val, new_val) -> {
if(bill.isSelected() == false) {
bill.setSelected(true);
} else {
bill.setSelected(false);
}
});
return new SimpleObjectProperty<>(checkBox);
}
}
In the controller class:
billsTable.setRowFactory(row -> new TableRow<Bill>(){
@Override
public void updateItem(Bill bill, boolean empty){
super.updateItem(bill, empty);
if (bill == null || empty) {
setStyle("");
} else {
//Now 'bill' has all the info of the Bill in this row
if (bill.isSelected() == true) {
//We apply now the changes in all the cells of the row
for(int i=0; i<getChildren().size();i++){
((Labeled) getChildren().get(i)).setTextFill(Color.GRAY);
}
//totalLabel.setText(String.format("%.2f", data.finalBillsTotal(bill.getAmount()))); // Update bill total - ** needs work
} else if(bill.isSelected() == false){
if(getTableView().getSelectionModel().getSelectedItems().contains(bill)){
for(int i=0; i<getChildren().size();i++){
((Labeled) getChildren().get(i)).setTextFill(Color.WHITE);;
}
}
}
}
}
}
The CheckBox on each row of the table is selected correctly. When one is unselected, the corresponding row characters change colour, but when another row is unselected, the previous row reverts back to its original colour. JavaFX is new to me so still trying to grasp the concepts. Any help is appreciated.
Solution
First of all you shouldn't use the cellValueFactory
to determine the visual representation of the table cell. That's the responsibility of the cellFactory
. The cellValueFactory
only exposes the data itself.
There already exists a TableCell
implementation + factory for this purpose: CheckBoxTableCell
.
The following code assumes your Bill
class provides a selectedProperty
method providing a BooleanProperty
containing the value of the selected
property (modified on a call of setSelected
).
checkBoxSelectionColumn.setCellValueFactory(cd -> cd.getValue().selectedProperty());
checkBoxSelectionColumn.setCellFactory(CheckBoxTableCell.forTableColumn(checkBoxSelectionColumn));
For modifying the look of the row I recommend using CSS and PseudoClass
:
final PseudoClass ghosted = PseudoClass.getPseudoClass("ghosted");
billsTable.setRowFactory(row -> new TableRow<Bill>(){
private final InvalidationListener l = o -> {
pseudoClassStateChanged(ghosted, getItem().isSelected());
};
private final WeakInvalidationListener listener = new WeakInvalidationListener(l);
@Override
public void updateItem(Bill bill, boolean empty){
// remove listener from last item
Bill oldItem = getItem();
if (oldItem != null) {
oldItem.selectedProperty().removeListener(listener);
}
super.updateItem(bill, empty);
if (bill == null || empty) {
pseudoClassStateChanged(ghosted, false);
} else {
// add listener & update
bill.selectedProperty().addListener(listener);
l.invalidated(null);
}
}
});
This allows you to modify the cell style from a css stylesheet added to the TableView
, one of its ancestors or the scene containing the table:
.table-row-cell:ghosted .table-cell {
-fx-text-fill: gray;
}
This has the additional benefit of allowing you to specify a different style for selected rows:
.table-row-cell:ghosted:selected .table-cell {
-fx-text-fill: red;
}
Answered By - fabian
Answer Checked By - Gilberto Lyons (JavaFixing Admin)