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
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 Observable
s. If any of those Observable
s 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.