代码之家  ›  专栏  ›  技术社区  ›  Zephyr

如何基于行项目的属性设置TableCell的样式?

  •  0
  • Zephyr  · 技术社区  · 7 年前

    我的申请有 TableView 其中填充了对图像文件的引用列表。

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

    在我的 表格视图 如果物理文件不存在,我想把我的“文件名”列红色的文本样式化。我已经实施了 CellFactory 它有点像…有时。这个 updateItem() 方法被重写以检查所涉文件的存在,但它并不总是正确的:一些行将用红色文本来命名,其他行则不会,即使它们指向同一个文件。

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

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

    主类

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

    小精灵

    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

    您可以看到,第一个“Log.PNG”被正确地标示为纯文本,但随后的条目似乎表明文件不存在。调整窗口大小或滚动列表有时也会导致样式文本从一行变为另一行;这是相当随机的。

    如何准确地测试由行表示的文件是否实际存在?

    附带问题: 在我的产品应用程序中,滚动非常慢而且延迟,因为在网络共享驱动器上检查文件的存在是缓慢的。有没有办法将这些项目作为第一次加载,而不是每次渲染单元格?

    1 回复  |  直到 7 年前
        1
  •  1
  •   fabian    7 年前

    关于不恰当的细胞类型:

    如果项目更改为现有的,则不会更改样式。这意味着如果您从一个非现有文件更改为现有文件,那么单元格文本仍然是红色的。你需要妥善处理这个案子:

    @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 / UNKNOWN )在一个 Task 安排在 ExecutorService