Как стилизовать TableCell на основе свойств элемента строки? - PullRequest
0 голосов
/ 24 июня 2018

В моем приложении есть TableView, заполненный списком ссылок на файлы изображений.

Данные загружаются из базы данных и просто предоставляют информацию о том, как найти сам файл изображения (так чтоуказывает подпапку и имя файла для изображения).

В моем TableView я хочу стилизовать текст моего столбца «Имя файла» красным, если физический файл не существует.Я реализовал CellFactory, и он "вроде" работает .. иногда.Метод updateItem() переопределяется для проверки наличия файла, о котором идет речь, но он не всегда корректен: некоторые строки будут выделены красным текстом, другие - нет, даже если они указывают на один и тот же файл.

Кроме того, при прокрутке списка значения могут изменяться время от времени.

Очень сложно описать, поэтому я создал 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 голосов
/ 24 июня 2018

Относительно неправильного стиля ячеек:

Вы не меняете стиль, если элемент заменяется на существующий. Это означает, что текст вашей ячейки остается красным, если вы переходите с несуществующего файла на существующий. Вы должны правильно разобраться с этим делом:

@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. Выполнение длительных операций, таких как связь с удаленным каталогом в этом потоке, приводит к тому, что пользовательский интерфейс не отвечает. Вы можете легко избежать проверки несколько раз, сохранив информацию либо в классе элемента iself, либо добавив некоторую структуру, содержащую данные, например, Map.

Связь с удаленным каталогом по-прежнему должна выполняться в отдельном потоке. Вы можете сделать привязку к состоянию файла (EXISTENT, NON_EXISTENT / UNKNOWN) в Task, запланированном в ExecutorService ...

...