如何根据行项目的属性设置TableCell的样式?

时间:2018-06-24 15:35:47

标签: java javafx tableview

我的应用程序有一个TableView,其中填充了对图像文件的引用列表。

数据是从数据库中加载的,仅提供有关如何定位图像文件本身的信息(因此它指示图像的子文件夹和文件名)。

在我的TableView中,如果物理文件不存在,我想将“文件名”列的文本设置为红色。我已经实现了CellFactory,并且它的“种类”是..有时。 updateItem()方法被覆盖以检查所讨论文件的存在,但是它并不总是正确的:有些行将用红色文本设置样式,而另一些则不会,即使它们指向同一文件。 / p>

此外,在滚动列表时,值可能会偶尔更改。

很难描述,所以我在下面创建了一个MCVE。

  

Main.java

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.File;

public class Main extends Application {

    private ObservableList<DataItem> dataItems;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        initData();

        // Main interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        // Setup the TableView
        TableView<DataItem> tableView = new TableView<>();
        TableColumn<DataItem, ImageCategory> colCategory = new TableColumn<>("Category");
        TableColumn<DataItem, String> colFilename = new TableColumn<>("Filename");

        // Initialize the column data
        colCategory.setCellValueFactory(cellData -> cellData.getValue().categoryProperty());
        colFilename.setCellValueFactory(cellData -> cellData.getValue().filenameProperty());
        tableView.getColumns().add(colCategory);
        tableView.getColumns().add(colFilename);

        // Style text based on file exists
        colFilename.setCellFactory(filenameCell -> new TableCell<DataItem, String>() {
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setText(null);
                    setStyle("");
                } else {

                    // Check if file exists
                    DataItem thisItem = getTableView().getItems().get(getIndex());
                    File imageFile = new File("C:\\Users\\XXX\\Desktop\\"
                            + thisItem.getCategory().getCategoryName() + "\\"
                            + item);
                    if (!imageFile.exists()) {
                        setStyle("-fx-text-fill: red");
                    }
                    setText(item);
                }

            }
        });

        tableView.setItems(dataItems);
        root.getChildren().add(tableView);

        primaryStage.setScene(new Scene(root));
        primaryStage.setWidth(300);
        primaryStage.setHeight(200);
        primaryStage.show();
    }

    private void initData() {

        dataItems = FXCollections.observableArrayList();

        for (int i = 0; i < 15; i++) {
            dataItems.add(new DataItem(
                    new ImageCategory(1, "Application Icon"),
                    "icon.png"));
        }
          for (int i = 0; i < 15; i++) {
            dataItems.add(new DataItem(
                    new ImageCategory(1, "Logo"),
                    "logo.png"));
        }
    }
}
  

DataItem.java

import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;

class DataItem {

    private SimpleObjectProperty<ImageCategory> category = new SimpleObjectProperty<>();
    private SimpleStringProperty filename = new SimpleStringProperty();

    public DataItem(ImageCategory category, String filename) {
        this.category.set(category);
        this.filename.set(filename);
    }

    public ImageCategory getCategory() {
        return category.get();
    }

    public SimpleObjectProperty<ImageCategory> categoryProperty() {
        return category;
    }

    public String getFilename() {
        return filename.get();
    }

    public SimpleStringProperty filenameProperty() {
        return filename;
    }
}
  

ImageCategory.java

import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;

class ImageCategory {
    private SimpleIntegerProperty categoryId = new SimpleIntegerProperty();
    private SimpleStringProperty categoryName = new SimpleStringProperty();

    public ImageCategory(int categoryId, String categoryName) {
        this.categoryId.set(categoryId);
        this.categoryName.set(categoryName);
    }

    public int getCategoryId() {
        return categoryId.get();
    }

    public SimpleIntegerProperty categoryIdProperty() {
        return categoryId;
    }

    public String getCategoryName() {
        return categoryName.get();
    }

    public SimpleStringProperty categoryNameProperty() {
        return categoryName;
    }

    @Override
    public String toString() {
        return getCategoryName();
    }
}

显然,您需要一个示例文件来对此进行测试。这是运行此结果的屏幕截图。请注意,Logo/logo.png 存在存在:

Screenshot of display issue

您看到第一个“ logo.png”的格式正确,为纯文本格式,但是随后的条目似乎表明该文件不存在。调整窗口大小或滚动列表有时还会导致样式化的文本从一行更改为另一行。这是相当随机的。

如何准确测试该行代表的文件是否确实存在?

侧问题::在我的生产应用程序中,滚动非常缓慢且延迟,因为在网络共享驱动器上检查文件是否存在速度很慢。有没有一种方法可以将这些项目的样式设置为首次加载,而不是每次渲染单元格时都设置样式?

1 个答案:

答案 0 :(得分:1)

关于不适当样式的电池:

如果该项已更改为现有样式,则无需更改样式。这意味着,如果您从不存在的文件更改为现有的文件,则单元格文本将保持红色。您需要正确处理这种情况:

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

    if (item == null || empty) {
        setText(null);
        setStyle("");
    } else {

        // Check if file exists
        DataItem thisItem = getTableView().getItems().get(getIndex());
        File imageFile = new File("C:\\Users\\XXX\\Desktop\\"
                + thisItem.getCategory().getCategoryName() + "\\"
                + item);
        if (!imageFile.exists()) {
            setStyle("-fx-text-fill: red");
        } else {
            // modification here ----------------------------------------------
            setStyle("");
        }
        setText(item);
    }

}

关于滚动问题:

updateItem在JavaFX应用程序线程上运行。执行长时间运行的操作(例如与该线程上的远程目录进行通信)会使UI无响应。您可以通过将信息存储在item类iself中或添加一些包含数据的结构来轻松避免多次检查。 Map

与远程目录的通信仍应在单独的线程上进行。您可以在EXISTENT上安排的NON_EXISTENT上执行文件状态(UNKNOWNTask / ExecutorService)的补充...