简体   繁体   中英

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.


Edit 2 To minimize the loc and enhance the overall maintainability, I've created a utilityClass that I call during initialization of the controllers.

public class CellFactoryUtil
{

    public <E> void setCellFactoryStringErrorCol(TableColumn<E, String> errorCol)
    {
        PseudoClass errorPC = PseudoClass.getPseudoClass("error-pc");

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

                    if (item != null && item.length() > 2)
                    {
                        setText(item);
                        pseudoClassStateChanged(errorPC, true);
                        setTooltip(new Tooltip("All/In work/Fixed"));
                    }
                    else
                    {
                        setText(null);
                        pseudoClassStateChanged(errorPC, false);
                    }
                }
            }
        );
    }
}

I'm using the generics because the program has several tables with different wrappers. Still a lot of code, but it works just fine!

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 : ie 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM