JavaFX TableView 触发 css ":filled"-Pseudo-class onScrolling

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

当我在 JavaFX8 TableView 中上下滚动时,越来越多的单元格被错误地着色。它接缝 css-pseudo-class ":filled" 被触发,尽管单元格应该是空的。我无法确定关联的 cellValueFactory 是否行为不当,或者某处是否有错误。

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);
        }
    }

}

你们中有人能想出导致这种行为的原因吗?或者确保单元格在滚动期间保持空白的方法?


顺便说一句,我宁愿不设置额外的 cellValueFactory,因为类似的指标太多了。我也试过了 - 意思是一样的结果。

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("");
                }
            }
        };
    });
}

非常感谢!


编辑 1 我可以使用 "James_D"s 的回答中的代码来解决我的问题。 尽管我宁愿使用 CSS。


编辑 2 为了最小化 loc 并增强整体可维护性,我创建了一个在控制器初始化期间调用的 utilityClass。

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);
                    }
                }
            }
        );
    }
}

我正在使用泛型,因为该程序有几个包含不同包装器的表。仍然有很多代码,但它工作得很好!

对于 CSS,您正在将样式 class 应用到 TableColumn:目前还不清楚它是如何工作的。 JavaFX CSS guide 中没有 TableColumn 的文档。做到这一点的方法是使用细胞工厂。

请注意,:filled 伪 class 与 :empty 正好相反:即它适用于除空 "space filler" 单元格之外的所有单元格table 但是在实际有数据的行下面。对于行中有数据的所有单元格,:filled 应该是 true,即使单元格数据是 null(当然如果单元格数据是空字符串)。

在您的单元工厂实施中,您永远不会从 class 样式列表中删除 "error-cell" 样式 class。此外,当您滚动时,您可能会将字符串 "error-cell" 的多个副本添加到样式列表 classes (可能导致内存泄漏)。

你需要这样的东西

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");
            }
        }
    };
});

请注意,为此创建您自己的伪 class 可能更简洁,而不是操纵样式 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);
        }
    }

});

然后 CSS 看起来像

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