I have a column in my JavaFX TableView where the value is just a color based on some other value. For this I use a custom TableCell to fill the color.
This works fine unless I refresh the items in the TableView. In that case cell just displays transparent or maybe nothing…
I created a sample to demonstrate this:
2 files:
import javafx.application.Application;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.util.Random;
public class ColorCellTest extends Application {
private final ListProperty<CellData> items = new SimpleListProperty<>(FXCollections.observableArrayList());
private final TableView<CellData> tableView = new TableView<>();
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 800, 600);
fillItems();
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS);
TableColumn<CellData, String> statCol = new TableColumn<>(" ");
statCol.setMaxWidth(20);
statCol.setCellFactory(param -> new ColorCell());
tableView.getColumns().add(statCol);
TableColumn<CellData, String> textCol = new TableColumn<>("Text");
textCol.setCellValueFactory(new PropertyValueFactory<>("text"));
tableView.getColumns().add(textCol);
tableView.setItems(items);
root.setCenter(tableView);
Button button = new Button("Refresh");
button.setOnAction(event -> refreshItems());
root.setBottom(button);
primaryStage.setScene(scene);
primaryStage.setTitle("ColorCellTest");
primaryStage.show();
}
private void fillItems() {
System.out.println("fill items");
Random random = new Random();
for (int j = 0; j < 20; j++) {
char c = (char) (random.nextInt(3) + 65);
String stat = Character.toString(c);
String text = "Text " + stat;
items.add(new CellData(stat, text));
}
}
private void refreshItems() {
System.out.println("refresh items");
items.clear();
fillItems();
}
private static class ColorCell extends TableCell<CellData, String> {
private final Circle overlay;
public ColorCell() {
overlay = new Circle();
overlay.setFill(Color.TRANSPARENT);
overlay.setRadius(4);
StackPane pane = new StackPane();
pane.getChildren().addAll(overlay);
setGraphic(pane);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
super.updateItem(item, empty);
if (!empty) {
if (getTableRow() != null) {
CellData currentRowValue = getTableRow().getItem();
if (currentRowValue != null) {
System.out.println("fill:" + currentRowValue.getText() + " " + currentRowValue.getStatus());
Paint paint = switch (currentRowValue.getStatus()) {
case "A" -> Color.GREEN;
case "B" -> Color.BLUE;
case "C" -> Color.RED;
default -> null;
};
overlay.setFill(paint);
}
}
} else {
setGraphic(null);
setText(null);
}
}
}
}
CellData class
public class CellData {
private String status;
private String text;
public CellData() {
}
public CellData(String status, String text) {
this.status = status;
this.text = text;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
All I’m doing is creating a custom TableCell class which draws a circle of a color based on the status.
This works until you refresh the list.
Any ideas?
TIA
jdk-22.0.1
JavaFX 22