GridView 仅在图像可见时创建图像

GridView only create Images when they would be visible

嘿嘿

所以我有以下问题。我有大约 1500 张扑克牌图像。我想将它们显示在“画廊”中,您可以在其中滚动浏览它们。我能够使用 ImageCell 对象创建一个 GridView,我也能够向它添加图像。现在我的问题是,如果我一次添加所有图像,逻辑上 Java 会因为堆错误而崩溃。我在列表中有图像 url(本地文件)。我怎么能实现它只加载让我们说 15 张图像。如果我然后滚动它加载下一个 15 并卸载旧的。所以它只会加载实际可见图像的图像,而不是全部 1500 张图像。我该怎么做?我完全没有想法。 Platform.runLater() 是必需的,因为 ControlsFX

存在某种问题

这是我的代码:

    public void initialize() {

    GridView<Image> gridView = new GridView<>();
    gridView.setCellFactory(gridView1 -> new ImageGridCell(true));
    Image image = new Image("C:\Users\nijog\Downloads\cardpictures\01DE001.png");

    gridView.setCellWidth(340);
    gridView.setCellHeight(512);

    //Platform.runLater(()-> {
    //    for (int i = 0; i < 5000; i++){
    //        gridView.getItems().add(image);
    //    }
    //});

    Platform.runLater(() -> gridView.getItems().addAll(createImageListFromCardFiles()));

    borderPane.setCenter(gridView);

}

protected List<Image> createImageListFromCardFiles(){

    List<Image> imageViewList = new ArrayList<>();
    App.getCardService().getCardArray().stream()
            //.filter(Card::isCollectible)
            .sorted(Comparator.comparingInt(Card::getCost))
            .sorted(Comparator.comparing(Card::isChampion).reversed())
            .skip(0)
            //.limit(100)
            .forEach(card -> {
                try {
                    String url = String.format(App.pictureFolderPath +"%s.png", card.getCardCode());
                    imageViewList.add(new Image(url));
                } catch (Exception e) {
                    System.out.println("Picture file not found [CardCode = " + card.getCardCode() + "]");
                    App.logger.writeLog(Logger.Operation.EXCEPTION, "Picture file not found [CardCode = " + card.getCardCode() + "]");
                }
            });
    return imageViewList;
}

您可能不需要使用您描述的策略。您在大小为 340x512(即 174,080 像素)的单元格中显示图像。图像存储是每个像素 4 个字节,所以这是每个图像 696,320 个字节;其中 1500 个将消耗大约 1GB。您只需要确保以您正在显示的尺寸加载图像(而不是其原始尺寸):

// imageViewList.add(new Image(url));
imageViewList.add(new Image(url, 340, 512, true, true, true));

如果您稍后需要全尺寸图像(例如,如果您希望用户 select 网格视图中的图像并将其显示在更大的窗格中),您只需要重新加载它来自 url.

如果您确实需要实施您描述的策略,GridView 支持开箱即用。只需保留一个 URL 列表,而不是 Image,并根据需要使用自定义 GridCell 加载图像。这将消耗更少的内存,但代价是更多 I/O(加载图像)和 CPU(解析图像格式)。

GridView 图像 url 制作项目,存储为字符串。

然后你可以这样做:

GridView<String> gridView = new GridView<>();
gridView.getItems().addAll(getAllImageURLs());

gridView.setCellFactory(gv -> new GridCell<>() {
    private final ImageView imageView = new ImageView();

    {
        imageView.fitWidthProperty().bind(widthProperty());
        imageView.fitHeightProperty().bind(heightProperty());
        imageView.setPreserveRatio(true);
    }

    @Override
    protected void updateItem(String url, boolean empty) {
        super.updateItem(url, empty);
        if (empty || url == null) {
            setGraphic(null);
        } else {
            double w = getGridView().getCellWidth();
            double h = getGridView().getCellHeight();
            imageView.setImage(new Image(url, w, h, true, true, true));
            setGraphic(imageView);
        }
    }
});


protected List<String> getAllImageURLs(){

    return App.getCardService().getCardArray().stream()
            // isn't the first sort redundant here?
            .sorted(Comparator.comparingInt(Card::getCost))
            .sorted(Comparator.comparing(Card::isChampion).reversed())
            .map(card -> String.format(App.pictureFolderPath +"%s.png", card.getCardCode()))
            .collect(Collectors.toList());
}