实际上,我认为可能有一种比您尝试使用的方法更好的通用方法来满足您的应用程序的要求,但这是我实现您描述的方法的最佳答案。
创建一个有界的BlockingQueue
在加载图像时保存它们。队列的大小可能需要一些调整:太小,您将没有任何“缓冲区”(因此您将无法利用任何比平均加载速度更快的缓冲区),太大,您可能会消耗太多内存。这BlockingQueue
允许您从多个线程安全地访问它。
创建一个简单循环并加载每个图像的线程同步地,即该线程在每个图像加载时阻塞,并将它们存储在BlockingQueue
.
由于您想要尝试在每个 FX 帧(即 60fps)中最多显示一次图像,因此请使用AnimationTimer http://docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.html。这有一个handle
在 FX 应用程序线程上的每个帧渲染上调用的方法,因此您可以实现它poll()
the 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);
}
}