JavaFX TableView запускает css «:заполненный» — включение псевдокласса

#css #javafx #tableview #pseudo-class

#css #javafx #tableview #псевдокласс

Вопрос:

когда я прокручиваю вверх и вниз в JavaFX8 TableView, все больше и больше ячеек неправомерно окрашиваются. Это означает, что css-псевдокласс «: заполненный» запускается, хотя ячейка должна быть пустой. Я не могу понять, отсутствует ли связанный cellValueFactory или где-то есть ошибка.

вот как это выглядит после прокрутки некоторое время

.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 amp;amp; 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». Хотя я бы предпочел использовать CSS.


Редактировать 2 Чтобы минимизировать loc и повысить общую удобство обслуживания, я создал класс утилит, который я вызываю во время инициализации контроллеров.

 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 amp;amp; item.length() > 2)
                    {
                        setText(item);
                        pseudoClassStateChanged(errorPC, true);
                        setTooltip(new Tooltip("All/In work/Fixed"));
                    }
                    else
                    {
                        setText(null);
                        pseudoClassStateChanged(errorPC, false);
                    }
                }
            }
        );
    }
}
  

Я использую дженерики, потому что в программе есть несколько таблиц с разными оболочками. Все еще много кода, но он работает просто отлично!

Комментарии:

1. Ну, вы все еще используете CSS с моим решением, нет? И у вас есть дополнительная функциональность на фабрике ячеек, которую вы не смогли бы реализовать без фабрики ячеек (всплывающая подсказка и отсутствие отображения значений с длиной <= 2), поэтому вам уже нужна фабрика ячеек. Не совсем уверен, что именно вы предпочли бы сделать.

Ответ №1:

Для CSS вы применяете класс style к TableColumn : не совсем понятно, как это будет работать. В руководстве по JavaFX CSS для нет документации TableColumn . Способ сделать это — использовать фабрику ячеек.

Обратите внимание, что :filled псевдокласс прямо противоположен :empty : т. Е. Он верен для всех ячеек, кроме пустых ячеек «заполнителя пространства», которые находятся в таблице, но под строками, которые на самом деле содержат данные. :filled должно быть true для всех ячеек, в строках которых есть данные, даже если данные ячейки есть null (и, конечно, если данные ячейки представляют собой пустую строку).

В вашей реализации cell factory вы никогда не удаляете "error-cell" класс style из списка классов стилей. Более того, при прокрутке вы, скорее всего, добавите несколько копий строки "error-cell" в список классов стилей (что может привести к утечке памяти).

Вам нужно что-то вроде

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

            if (item != null amp;amp; 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");
            }
        }
    };
});
  

Обратите внимание, что для этого может быть проще создать свой собственный псевдокласс, вместо того, чтобы манипулировать классом стиля:

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

Комментарии:

1. Ваше обновление отлично работает! Еще раз спасибо за быстрый и глубокий ответ.