Michael Sachsenhauser Michael Sachsenhauser - 2 months ago 33
CSS Question

JavaFX TableView triggers css ":filled"-Pseudo-class onScrolling

when I scroll up and down in a JavaFX8 TableView, more and more cells get wrongfully colorized. It seams that the css-pseudo-class ":filled" gets triggered although the cell should be empty. I can't figure out if the associated cellValueFactory missbehaves, or if there's an error somewhere.

this is how it looks after scrolling a while

.css

error-cell:filled {
-fx-background-color: #ff3333;
-fx-text-fill: white;
-fx-opacity: 1;
}


.fxml

<BorderPane fx:id="projectList" stylesheets="@../css/projectList.css,@../css/tables.css" xmlns="http://javafx.com/javafx/8.0.45" xmlns:fx="http://javafx.com/fxml/1" fx:controller="my.package.ProjectListController">
<center>
<TableView fx:id="projectListTableView">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
<columns>

<TableColumn fx:id="errorCol" styleClass="error-cell" text="Error">
<cellValueFactory>
<PropertyValueFactory property="error" />
</cellValueFactory>
</TableColumn>

</columns>
</TableView>
</center>
</BorderPane>


.java

public class ProjectListController
{
@FXML
private TableView<ProjectWrapper> projectListTableView;

@FXML
private TableColumn<ProjectWrapper, String> errorCol;

private ObservableList<ProjectWrapper> projectTable = FXCollections.observableArrayList();

// dummy-method that fills projectTable, normally it's a db-query
public void fillErrorColumn()
{
ProjectWrapper pw = new ProjectWrapper();
pw.setError("1/0/0");
this.projectTable.add(pw);
}


public void populateTable()
{
try
{
projectListTableView.setItems(this.projectTable);
}
catch (Exception e)
{
logger.error("", e);
}
}

}


Can anyone of you think of a reason that causes this behaviour? Or a way to ensure that the cells stay empty during scrolling?




Btw, I'd rather not set an extra cellValueFactory, because there are way too many similar indicators. I've also tried it in vain - meaning same outcome.

private void setCellFactoryErrorCol()
{
errorCol.setCellFactory(column ->
{
return new TableCell<ProjectWrapper, String>()
{
@Override
protected void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);

if (item != null && item.length() > 2)
{
setText(item); // ohne setText() bleibt das Feld
// leer - wird nicht automatisch von
// super gesetzt
getStyleClass().add("error-cell");
setTooltip(new Tooltip("All/In work/Fixed"));
}
else
{
setText(null);
setStyle("");
}
}
};
});
}


Thank you very much!




Edit 1
I could solve my problem by using the code from "James_D"s' answer.
Even though I'd have rather used CSS.

Answer

For the CSS, you are applying the style class to the TableColumn: it is not really clear how that is going to work. There is no documentation in the JavaFX CSS guide for TableColumn. The way to do this is with a cell factory.

Note that the :filled pseudoclass is just the opposite of :empty: i.e. it is true for all cells except the empty "space filler" cells that are in the table but underneath the rows that actually have data. :filled should be true for all cells that have data in the rows, even if the cell data is null (and certainly if the cell data is an empty string).

In your cell factory implementation, you never remove the "error-cell" style class from the list of style classes. Moreover, as you scroll, you will likely add multiple copies of the string "error-cell" to the list of style classes (potentially causing a memory leak).

You need something like

errorCol.setCellFactory(column ->
{
    return new TableCell<ProjectWrapper, String>()
    {
        @Override
        protected void updateItem(String item, boolean empty)
        {
            super.updateItem(item, empty);

            if (item != null && item.length() > 2)
            {
                setText(item); // ohne setText() bleibt das Feld
                                // leer - wird nicht automatisch von
                                // super gesetzt
                if (! getStyleClass().contains("error-cell")) {
                    getStyleClass().add("error-cell");
                }
                setTooltip(new Tooltip("All/In work/Fixed"));
            }
            else
            {
                setText(null);
                setStyle(""); // not sure this line does anything: you never set the style anyway...
                getStyleClass().remove("error-cell");
            }
        }
    };
});

Note that it might be cleaner to create your own pseudoclass for this, instead of manipulating the style class:

PseudoClass errorPC = PseudoClass.getPseudoClass("error");

errorCol.setCellFactory(column -> new TableCell<ProjectWrapper, String>() {

    @Override
    protected void updateItem(String item, boolean empty)
    {
        super.updateItem(item, empty);

        if (item != null && item.length() > 2)
        {
            setText(item); // ohne setText() bleibt das Feld
                            // leer - wird nicht automatisch von
                            // super gesetzt
            pseudoClassStateChanged(errorPC, true);
            setTooltip(new Tooltip("All/In work/Fixed"));
        }
        else
        {
            setText(null);
            pseudoClassStateChanged(errorPC, false);
        }
    }

});

and then the CSS looks like

.table-cell:error {
  -fx-background-color: #ff3333;
  -fx-text-fill: white;
  -fx-opacity: 1;
}
Comments