JavaFX загружает системные иконки в фоновом потоке - PullRequest
0 голосов
/ 06 октября 2018

В моем приложении есть компонент файлового браузера (TableView), и я загружаю значки файлов с помощью FileSystemView.getFileSystemView().getSystemIcon().Теперь это довольно медленно, так как мне также нужно преобразовать значок в BufferedImage, а в JavaFX Image.Поэтому мне нужно было выдвинуть это все в фоновые потоки.Я создал IconLoadingTask, который загружает один значок - и во время updateItem() TableView я помещаю эти IconLoadingTasks в ExecutorService.Это работает, но может быть улучшено.

Проблема, с которой я столкнулся, заключается в том, что если в папке много значков, а пользователь быстро перетаскивает полосу прокрутки, загружается множество «лишних» значков, что приводит к остановке потока (-ов).) и приводит к тому, что часто не видны значимые в то время значки: те, которые в настоящее время отображаются в TableView.

У кого-нибудь есть идеи, как решить эту проблему?Я думал о доступе к вертикальной полосе прокрутки TableView и прослушивании ее (и, возможно, обновлении только после того, как прокрутка была выпущена), но я не уверен, как даже получить доступ к полосе прокрутки ... и, возможно, есть более простое решение, которое ускользает от меня.

РЕДАКТИРОВАТЬ:

Ну, хорошо.Я создал минимальный, полный и проверяемый пример из своего кода Kotlin в код Java, и я больше не могу воспроизвести «эффект».Похоже, сейчас работает так, как я и предполагал.Кажется, мне просто нужно выяснить, что «лишнего» я ​​делаю в коде моего основного приложения.В любом случае вот примерный рабочий код.

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.filechooser.FileSystemView;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class IconLoadTest extends Application {

    public static class FileEntry {
        public Path path;
        private Image icon;

        public FileEntry(Path path) {
            this.path = path;
        }

        synchronized void setIcon(Image icon) {
            this.icon = icon;
        }

        synchronized Image getIcon() {
            return icon;
        }

        public void setPath(Path path) {
            this.path = path;
        }

        public Path getPath() {
            return path;
        }
    }

    class MyTableCell extends TableCell<FileEntry, Path> {
        @Override
        protected void updateItem(Path item, boolean empty) {
            super.updateItem(item, empty);

            if (empty || item == null) {
                setText("");
                setGraphic(null);
                return;
            }

            setText(item.getFileName().toString());

            Image icon = dataMap.get(item).getIcon();
            if (icon == null) {
                setGraphic(null);
                executor.execute(new IconLoadingTask(item));
            } else {
                setGraphic(new ImageView(icon));
            }
        }
    }

    class IconLoadingTask extends Task<Void>  {

        private Path path;

        IconLoadingTask(Path path) {
            this.path = path;
        }

        @Override
        protected Void call() {

            FileEntry entry = dataMap.get(path);
            if (entry.icon != null) {
                return null;
            }

            entry.setIcon(getIcon(path.toFile()));

            // Refresh currently visible items
            Platform.runLater(() -> table.refresh());

            return null;
        }
    }

    private TableView<FileEntry> table = new TableView<>();

    // Table data
    private HashMap<Path, FileEntry> dataMap = new HashMap<>();
    private List<FileEntry> data = FXCollections.observableArrayList();

    private ExecutorService executor = Executors.newFixedThreadPool(1);

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        root.getChildren().add(table);

        TableColumn<FileEntry, Path> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(new PropertyValueFactory<>("path"));
        table.getColumns().add(nameCol);
        nameCol.setCellFactory(tableColumn -> new MyTableCell());

        // Sort so that dirs come first
        nameCol.setComparator((o1, o2) -> {
            if (Files.isDirectory(o1) && !Files.isDirectory(o2)) {
                return -1;
            } else if (!Files.isDirectory(o1) && Files.isDirectory(o2)) {
                return 1;
            } else {
                return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase());
            }
        });

        // Set to a directory with lots of files (e.g. System32 on Windows)
        String directory = "C:\\Windows\\System32\\";

        // Load files from directory, and create entries for table
        Path dir = Paths.get(directory);
        List<Path> files = listContents(dir);
        for (Path p : files) {
            FileEntry entry = new FileEntry(p);
            data.add(entry);
            dataMap.put(p, entry);
        }
        table.setItems((ObservableList<FileEntry>) data);

        // Sort
        table.getSortOrder().add(table.getColumns().get(0));
        table.sort();

        // Display the app
        Scene scene = new Scene(root, 600, 480);
        primaryStage.setTitle("Icon Background Loading");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    // Gets directory contents
    private static List<Path> listContents(Path directory) {
        ArrayList<Path> paths = new ArrayList<>();
        try {
            Files.newDirectoryStream(directory).forEach(paths::add);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return paths;
    }

    // Gets a system icon for a file
    private static Image getIcon(File file) {
        Icon ico = FileSystemView.getFileSystemView().getSystemIcon(file);
        java.awt.Image awtImage = ((ImageIcon) ico).getImage();
        BufferedImage bImg;
        if (awtImage instanceof BufferedImage) {
            bImg = (BufferedImage) awtImage;
        } else {
            bImg = new BufferedImage(
                    awtImage.getWidth(null),
                    awtImage.getHeight(null),
                    BufferedImage.TYPE_INT_ARGB
            );
            Graphics graphics = bImg.createGraphics();
            graphics.drawImage(awtImage, 0, 0, null);
            graphics.dispose();
        }
        return SwingFXUtils.toFXImage(bImg, null);
    }

    public static void main(String[] args) {
        Application.launch(IconLoadTest.class, args);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...