Colouring table row in JavaFX - javafx

This question is related to this. Now I want to colour the row where field value equals to some value.
#FXML
private TableView<FaDeal> tv_mm_view;
#FXML
private TableColumn<FaDeal, String> tc_inst;
tc_inst.setCellValueFactory(cellData -> new SimpleStringProperty(""+cellData.getValue().getInstrumentId()));
tc_inst.setCellFactory(column -> new TableCell<FaDeal, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item);
// Style row where balance < 0 with a different color.
TableRow currentRow = getTableRow();
if (item.equals("1070")) {
currentRow.setStyle("-fx-background-color: tomato;");
} else currentRow.setStyle("");
}
}
});
The problem is I don't want to show tc_inst in my table. For this reason I set visible checkbox in SceneBuilder to false. In this case colouring part doesn't work at all. How can hide tc_inst so that colouring works?

Use a row factory, instead of a cell factory, if you want to change the color of the whole row:
tv_mm_view.setRowFactory(tv -> new TableRow<FaDeal>() {
#Override
public void updateItem(FaDeal item, boolean empty) {
super.updateItem(item, empty) ;
if (item == null) {
setStyle("");
} else if (item.getInstrumentId().equals("1070")) {
setStyle("-fx-background-color: tomato;");
} else {
setStyle("");
}
}
});
Note that if the value of instrumentId changes while the row is displayed, then the color will not change automatically with the above code, unless you do some additional work. The simplest way to make that happen would be to construct your items list with an extractor which returned the instrumentIdProperty() (assuming you are using the JavaFX property pattern in FaDeal).

Related

Cell dependent appearance of cell in JavaFX

How can I change the appearance of a cell based on the status of another cell in the same column of a tableView in JavaFX.
colHidden.setCellValueFactory(param -> {
return param.getValue().hiddenProperty();
});
colHidden.setCellFactory(tc -> new CheckBoxTableCell<>());
colLabel.setCellFactory(...);
I have a cell which contains CheckBoxTableCell<>(). When I select this checkbox the content of the cell colLabel should be replaced by asterisks.
You can do something like this. The basic idea here is to "remember" the property corresponding to the check box in the same row, assuming the cell is not empty, and add a listener to it that updates the text. Then remove the listener from the previous property whenever the cell updates.
colLabel.setCellFactory(col -> new TableCell<RowType, ColumnType>() {
private ObservableValue<Boolean> hiddenProperty ;
ChangeListener<Boolean> listener = (obs, wasHidden, isNowHidden) -> updateText(isNowHidden);
#Override
protected void updateItem(ColumnType item, boolean empty) {
super.updateItem(item, empty);
if (hiddenProperty != null) {
hiddenProperty.removeListener(listener);
}
if (empty) {
setText(null);
hiddenProperty = null ;
} else {
hiddenProperty = getTableView().getItems().get(getIndex()).hiddenProperty();
hiddenProperty.addListener(listener);
updateText(hiddenProperty.get());
}
}
private void updateText(boolean hidden) {
if (hidden) {
setText("********");
} else {
setText(getItem().toString()); // or other format for string, etc
}
}
}
Replace RowType and ColumnType with the actual types used by colLabel (i.e. this assumes you have TableColumn<RowType, ColumnType> colLabel ;).

JavaFX Multiple cellValueFactories for one tableRow?

I'm newish to JavaFX still and don't quite understand how factories work.
What I'm trying to do is both apply styling to a tableCell in a tableColumn depending on what the String value is, and also make the cell editable.
Right now my code looks like this:
notesColumn.setCellFactory(column -> new TableCell<Computer, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null | empty)
{
setText(null);
setStyle("");
}
else
{
setText(item);
if (item.contains("Restoring #")) setTextFill(Color.CRIMSON);
else if (item.contains("Ready")) setTextFill(Color.FORESTGREEN);
else setTextFill(Color.BLACK);
}
}
});
And this piece works as I'd like it to, but if I try to make the cell editable by using the below code, the cellFactories overwrite one another, of course. How do I combine the two?
notesColumn.setCellFactory(TextFieldTableCell.forTableColumn());
The following should work:
notesColumn.setCellFactory(column -> new TextFieldTableCell<Computer, String>(new DefaultStringConverter()) {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null | empty)
{
setStyle("");
}
else
{
if (item.contains("Restoring #")) setTextFill(Color.CRIMSON);
else if (item.contains("Ready")) setTextFill(Color.FORESTGREEN);
else setTextFill(Color.BLACK);
}
}
});

javaFX tableview can not correct render row

if I add more items into table until it generate a scroll bar. At this time, when I scrolled scroll bar, first name not equal "Jacob" was rendered yellow.
enter image description here
table.setRowFactory(row -> new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
} else {
if (item.getFirstName().equals("Jacob")) {
setStyle("-fx-background-color: yellow");
}
}
}
});
When you scroll, the rows are reused to represent new items that are displayed. Your row factory never removes a style once it is set, so any rows that were yellow remain yellow no matter which items they are reused for.
In other words, once a particular row displays a person with first name "Jacob", that row will turn yellow, and then the style will never change (it will always stay yellow), even if the row is reused to display another person.
You need to reset the style if it the row should not be yellow:
table.setRowFactory(row -> new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setStyle("");
} else {
if (item.getFirstName().equals("Jacob")) {
setStyle("-fx-background-color: yellow");
} else {
setStyle("");
}
}
}
});

How to force TableRow repaint

How to force TableRow repaint ?.
Imagine the following scenario: The tableView is updated every 180 milliseconds, but the cell that receives the TableRow style information is not visible, and every time it is upgraded TableRow needs to be repainted. When I use refresh() method, it does not look good, especially with the mouse positioning on the TableView, it blinks and in this case consuming cpu.
myTableView.setRowFactory( new Callback<TableView, TableRow<Line>>() {
#Override
public TableRow call(final TableView p) {
return new TableRow<Line>() {
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
if(item.statusProperty().getValue().equals("BORDER")) {
setStyle("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;");
}
}
}
};
}
});
Since the style depends on the statusProperty() of the Line, which is observable, you can use a binding:
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings
.when(item.statusProperty().isEqualTo("BORDER"))
.then("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;")
.otherwise(""));
} else {
styleProperty().unbind();
setStyle("");
}
}
An alternative way to create the binding, which is probably more convenient if the logic is more complicated, is
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings.createStringBinding(() -> {
if ("BORDER".equals(item.getStyle())) {
return "-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;" ;
} else {
return "" ;
}
}, item.statusProperty());
} else {
styleProperty().unbind();
setStyle("");
}
}
This way the table row will observe the current item's status property, and automatically update the style if that property changes.
If you really want to make the code cleaner, of course, you should move the styles to an external CSS file. You can create a CSS PseudoClass (or more than one) which you can set and unset on the row:
final PseudoClass borderPC = PseudoClass.getPseudoClass("border");
myTableView.setRowFactory(p -> {
TableRow<Line> row = new TableRow<>();
ChangeListener<String> statusListener = (obs, oldStatus, newStatus) ->
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newStatus)) ;
row.itemProperty().addListener((obs, oldLine, newLine) -> {
if (oldLine != null) {
oldLine.statusProperty().removeListener(statusListener);
}
if (newLine == null) {
row.pseudoClassStateChanged(borderPC, false);
} else {
newLine.statusProperty().addListener(statusListener);
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newLine.getStatus()));
}
};
return row ;
});
Then in your external CSS file, do
.table-row-cell:border {
-fx-border-color:green;
-fx-border-width:2;
-fx-opacity:1;
}
Again, you can easily add more psuedoclasses, more rules to the CSS, and additional tests and pseudoclass updates using this approach.

Changing background color (or just color) of row (javafx)

I've a TableView. I want to change background color of rows according to some condition. For instance, if balance (getBalance()) is less than zero - set background color of that row to red. Here is my setCellValueFactory:
tc_proj_number.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getId().toString()));
tc_proj_date.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getValueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString()));
tc_proj_amount.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getBalance().setScale(2).toPlainString()));
tc_proj_comment.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getComment()));
Use TableColumn#setCellFactory method.
Try the following code (not tested):
tc_proj_amount.setCellFactory(column -> {
return new TableCell<Account, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item);
// Style row where balance < 0 with a different color.
BigDecimal balance = new BigDecimal(item);
TableRow currentRow = getTableRow();
if (balance.compareTo(BigDecimal.valueOf(0)) < 0) {
currentRow.setStyle("-fx-background-color: red;");
} else currentRow.setStyle("");
}
}
};
});

Resources