JavaFX – change Row background color in TableView dynamically based on item

I have a TableView that contains the item Person and can be filtered by a TextField.

This table view is filtered and sorted based on selected and inFilter, if the item doesn’t match the filter but is selected it will be added anyway at the bottom of the list but with a gray background.

I want to create change the background of the rows that match a such criteria, however the background is only updated the first time I change the filter, the following times it stays normal.

How do I make it update the background color every time the filter change?

I add below some relevant code.

Person.java

public class Person {     final SimpleBooleanProperty selected = new SimpleBooleanProperty(false);     final SimpleStringProperty name = new SimpleStringProperty("");     final SimpleStringProperty surname = new SimpleStringProperty("");     final SimpleBooleanProperty inFilter = new SimpleBooleanProperty(false);      public Person(Boolean selected, String name, String surname, Boolean inFilter) {         setSelected(selected);         setName(name);         setSurname(surname);         setInFilter(inFilter);     }      public boolean isSelected() {         return selected.get();     }      public SimpleBooleanProperty selectedProperty() {         return selected;     }      public void setSelected(boolean selected) {         this.selected.set(selected);     } ... } 

Controller.java

tableView.setRowFactory(tv -> new TableRow<Person>() {     @Override     protected void updateItem(Person p, boolean empty) {         super.updateItem(p, empty);         if (p == null)             setStyle("");         else if (p.isSelected() && !p.isInFilter())             setStyle("-fx-background-color: gray;");         else             setStyle("");     } }); 

For convenience I’ve posted the full code here: https://gist.github.com/Luigigimmi/6e06bb46fbccf13cb8116fd10c4f819f

Add Comment
1 Answer(s)

Use bindings in the TableRow implementation:

tableView.setRowFactory(tv -> new TableRow<Person>() {     @Override     protected void updateItem(Person p, boolean empty) {         super.updateItem(p, empty);         styleProperty().unbind();         if (p == null)             setStyle("");         else              styleProperty().bind(Bindings.createStringBinding(() -> {                 if (p.isSelected() && !p.isInFilter())                      return "-fx-background-color: gray;";                 else                     return "" ;             }, p.selectedProperty(), p.inFilterProperty()));     } }); 

The way this works is the Bindings.createStringBinding() method takes a function evaluating to a string (which will be used for the table row’s style), followed by a list of Observables. If any of those Observables change, the function will be re-evaluated, and the style updated.

Since the row may be reused for different Person objects in the table, you need to unbind the style from the old binding and rebind it when that happens.

Answered on July 15, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.