JavaFX背景下的图像加载和线程

 2023-02-16    382  

问题描述

我认为这是一个简单的问题,但是我很难找到答案.我有一个与Javafx场景对象关联的单个ImageView对象,我想从磁盘中加载大图像,并使用ImageView以接一个地显示它们.我一直在尝试找到一种重复检查图像对象的好方法,以及在后台加载何时将其设置为ImageView,然后开始加载新的图像对象.我提出的代码有时(下)有时可以使用,有时则没有.我很确定我会遇到Javafx和线程问题.它有时会加载第一个图像并停止.变量”处理”是类Boolean实例变量.

在后台加载图像并在加载后将图像加载并将其设置为imageview的正确方法是什么?

JavaFX背景下的图像加载和线程

public void start(Stage primaryStage) {

       ... 

       ImageView view = new ImageView();
       ((Group)scene.getRoot()).getChildren().add(view);

       ... 

        Thread th = new Thread(new Thread() {
            public void run() {

                while(true) {
                    if (!processing) {

                        processing = true;         
                        String filename = files[count].toURI().toString();
                        Image image = new Image(filename,true);

                        image.progressProperty().addListener(new ChangeListener<Number>() {
                            @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number progress) {
                                if ((Double) progress == 1.0) {
                                    if (! image.isError()) {
                                        view.setImage(image);
                                    }
                                    count++;
                                    if (count == files.length) {
                                        count = 0;
                                    }
                                    processing = false;
                                }
                            }
                        });
                    }
                }
            }
        });
    }

推荐答案

我实际上认为,与您要使用的方法相比,可能有更好的一般方法来满足您的应用程序的要求,但这是我实施您描述的方法的最佳答案.

创建一个有界的BlockingQueue在加载图像时将其保存.队列的大小可能需要进行一些调整:太小,您将没有任何”缓冲区”(因此您将无法利用任何比平均值快的速度),太大了,您可能会消耗过多的记忆. BlockingQueue允许您从多个线程安全地访问它.

创建一个线程,简单地循环并加载每个图像同步,即,在每个图像加载时,该线程会阻止并将其存放在BlockingQueue中.

.

由于您想尝试每次FX帧(即60fps)最多显示图像,请使用 AnimationTimer .这具有在每个帧渲染,FX应用程序线程上调用的handle方法,因此您可以将其实现为poll() poll() BlockingQueue,如果有图像可用,请将其设置在ImageView>中. .

这是SSCCE.我还指示了如何在固定时间显示每个图像的地方进行此操作,因为我认为这是一个更常见的用例,可能会帮助其他人寻求类似功能.

import java.io.File;
import java.net.MalformedURLException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javafx.animation.AnimationTimer;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;


public class ScreenSaver extends Application {

    @Override
    public void start(Stage primaryStage) {

        BorderPane root = new BorderPane();

        Button startButton = new Button("Choose image directory...");
        startButton.setOnAction(e -> {
            DirectoryChooser chooser= new DirectoryChooser();
            File dir = chooser.showDialog(primaryStage);
            if (dir != null) {
                File[] files = Stream.of(dir.listFiles()).filter(file -> {
                    String fName = file.getAbsolutePath().toLowerCase();
                    return fName.endsWith(".jpeg") | fName.endsWith(".jpg") | fName.endsWith(".png");
                }).collect(Collectors.toList()).toArray(new File[0]);
                root.setCenter(createScreenSaver(files));
            }
        });

        root.setCenter(new StackPane(startButton));

        primaryStage.setScene(new Scene(root, 800, 800));
        primaryStage.show();
    }

    private Parent createScreenSaver(File[] files) {
        ImageView imageView = new ImageView();
        Pane pane = new Pane(imageView);
        imageView.fitWidthProperty().bind(pane.widthProperty());
        imageView.fitHeightProperty().bind(pane.heightProperty());
        imageView.setPreserveRatio(true);

        Executor exec = Executors.newCachedThreadPool(runnable -> {
            Thread t = new Thread(runnable);
            t.setDaemon(true);
            return t ;
        });

        final int imageBufferSize = 5 ;
        BlockingQueue<Image> imageQueue = new ArrayBlockingQueue<Image>(imageBufferSize);

        exec.execute(() -> {
            int index = 0 ;
            try {
                while (true) {
                    Image image = new Image(files[index].toURI().toURL().toExternalForm(), false);
                    imageQueue.put(image);
                    index = (index + 1) % files.length ;
                }
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // This will show a new image every single rendering frame, if one is available: 
        AnimationTimer timer = new AnimationTimer() {

            @Override
            public void handle(long now) {
                Image image = imageQueue.poll();
                if (image != null) {
                    imageView.setImage(image);
                }
            }
        };
        timer.start();

        // This wait for an image to become available, then show it for a fixed amount of time,
        // before attempting to load the next one:

//        Duration displayTime = Duration.seconds(1);
//        PauseTransition pause = new PauseTransition(displayTime);
//        pause.setOnFinished(e -> exec.execute(createImageDisplayTask(pause, imageQueue, imageView)));
//        exec.execute(createImageDisplayTask(pause, imageQueue, imageView));

        return pane ;
    }

    private Task<Image> createImageDisplayTask(PauseTransition pause, BlockingQueue<Image> imageQueue, ImageView imageView) {
        Task<Image> imageDisplayTask = new Task<Image>() {
            @Override
            public Image call() throws InterruptedException {
                return imageQueue.take();
            }
        };
        imageDisplayTask.setOnSucceeded(e -> {
            imageView.setImage(imageDisplayTask.getValue());
            pause.playFromStart();
        });
        return imageDisplayTask ;
    }

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

以上所述是小编给大家介绍的JavaFX背景下的图像加载和线程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/34094.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。