JavaFX 一张一张显示多个 gif
JavaFX display multiple gifs one by one
我想一张张显示不同的gif。
我的想法是使用时间轴动态更改显示包裹在图像中的 gif。
但是我得到一个错误:
local variables referenced from a lambda expression must be final or
effectively final
有什么解决办法吗?
我的代码:
public class Task10 extends Application {
@Override
public void start(Stage primaryStage) {
HBox hbox5 = new HBox();
VBox VBoxAll = new VBox();
Image gifs[] = new Image[3];
gifs[0] = new Image(this.getClass().getResource("/img/L1.gif").toExternalForm());
gifs[1] = new Image(this.getClass().getResource("/img/L2.gif").toExternalForm());
gifs[2] = new Image(this.getClass().getResource("/img/L1.gif").toExternalForm());
ImageView currentGif = new ImageView();
Button localLoadButton = new Button("Start!");
localLoadButton.setOnAction(e -> {
show(currentGif, gifs);
});
hbox5.getChildren().addAll(currentGif, localLoadButton);
VBoxAll.getChildren().addAll(hbox5);
VBoxAll.setSpacing(15);
Pane pane = new Pane();
pane.getChildren().add(VBoxAll);
Scene scene = new Scene(pane, 500, 350);
primaryStage.setScene(scene);
primaryStage.show();
}
public void show(ImageView currentGif, Image[] gifs) {
for (int i = 0; i<gifs.length; i++) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, e -> { currentGif.setImage(gifs[i]); }),
new KeyFrame(Duration.seconds(2), e -> { currentGif.setImage(null); })
);
timeline.play();
}
}
}
在 Java 中,在 lambda 或匿名 class 外部声明但在所述 lambda 或匿名 class 中使用的局部变量必须是 "effectively final"。这意味着局部变量永远不会被修改(即重新分配)。如果局部变量可以在声明中添加 final
关键字而不会导致编译错误,则该变量实际上是最终变量。
在您的代码中,您具有以下内容:
public void show(ImageView currentGif, Image[] gifs) {
for (int i = 0; i<gifs.length; i++) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, e -> { currentGif.setImage(gifs[i]); }),
new KeyFrame(Duration.seconds(2), e -> { currentGif.setImage(null); })
);
timeline.play();
}
}
您在 lambda 中引用局部变量 i
(第一个 KeyFrame
)。问题是 for
循环修改了 i
(例如 i++
),这意味着该变量实际上不是最终变量。一种可能的解决方法是在循环内声明另一个变量,为其分配 i
的值,然后在 lambda 中使用该新的(不变的!)变量。然而,这不是唯一的问题。
您的代码正在为 gifs
数组中的每个元素创建一个单独的 Timeline
— 换句话说,每个循环一个。您还可以在每次迭代结束时对每个 Timeline
调用 play
。这将导致同时播放多个 Timeline
。正如您所想象的那样,这不会如您所愿。相反,你应该创建一个 single Timeline
并且让 Image
的每个变化都是它自己的 KeyFrame
。类似于以下内容:
public void show(ImageView view, Image[] gifs, Duration displayDuration) {
Timeline timeline = new Timeline();
Duration totalDelay = Duration.ZERO;
for (Image gif : gifs) {
KeyFrame frame = new KeyFrame(totalDelay, e -> view.setImage(gif));
timeline.getFrames().add(frame);
totalDelay = totalDelay.add(displayDuration);
}
timeline.getFrames().add(new KeyFrame(totalDelay, e -> view.setImage(null));
timeline.play();
}
这将以 displayDuration
间隔将数组中的 Image
更改为下一个 Image
。
如果您想知道为什么 gif
被允许在 lambda 内部使用——尽管显然被修改了——这是因为我使用了 "enhanced for loop"。增强型 for 循环的变量与常规 for
循环的处理方式不同,因为它在每次迭代时 初始化 。有关详细信息,请参阅 。
我想一张张显示不同的gif。
我的想法是使用时间轴动态更改显示包裹在图像中的 gif。
但是我得到一个错误:
local variables referenced from a lambda expression must be final or effectively final
有什么解决办法吗?
我的代码:
public class Task10 extends Application {
@Override
public void start(Stage primaryStage) {
HBox hbox5 = new HBox();
VBox VBoxAll = new VBox();
Image gifs[] = new Image[3];
gifs[0] = new Image(this.getClass().getResource("/img/L1.gif").toExternalForm());
gifs[1] = new Image(this.getClass().getResource("/img/L2.gif").toExternalForm());
gifs[2] = new Image(this.getClass().getResource("/img/L1.gif").toExternalForm());
ImageView currentGif = new ImageView();
Button localLoadButton = new Button("Start!");
localLoadButton.setOnAction(e -> {
show(currentGif, gifs);
});
hbox5.getChildren().addAll(currentGif, localLoadButton);
VBoxAll.getChildren().addAll(hbox5);
VBoxAll.setSpacing(15);
Pane pane = new Pane();
pane.getChildren().add(VBoxAll);
Scene scene = new Scene(pane, 500, 350);
primaryStage.setScene(scene);
primaryStage.show();
}
public void show(ImageView currentGif, Image[] gifs) {
for (int i = 0; i<gifs.length; i++) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, e -> { currentGif.setImage(gifs[i]); }),
new KeyFrame(Duration.seconds(2), e -> { currentGif.setImage(null); })
);
timeline.play();
}
}
}
在 Java 中,在 lambda 或匿名 class 外部声明但在所述 lambda 或匿名 class 中使用的局部变量必须是 "effectively final"。这意味着局部变量永远不会被修改(即重新分配)。如果局部变量可以在声明中添加 final
关键字而不会导致编译错误,则该变量实际上是最终变量。
在您的代码中,您具有以下内容:
public void show(ImageView currentGif, Image[] gifs) {
for (int i = 0; i<gifs.length; i++) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, e -> { currentGif.setImage(gifs[i]); }),
new KeyFrame(Duration.seconds(2), e -> { currentGif.setImage(null); })
);
timeline.play();
}
}
您在 lambda 中引用局部变量 i
(第一个 KeyFrame
)。问题是 for
循环修改了 i
(例如 i++
),这意味着该变量实际上不是最终变量。一种可能的解决方法是在循环内声明另一个变量,为其分配 i
的值,然后在 lambda 中使用该新的(不变的!)变量。然而,这不是唯一的问题。
您的代码正在为 gifs
数组中的每个元素创建一个单独的 Timeline
— 换句话说,每个循环一个。您还可以在每次迭代结束时对每个 Timeline
调用 play
。这将导致同时播放多个 Timeline
。正如您所想象的那样,这不会如您所愿。相反,你应该创建一个 single Timeline
并且让 Image
的每个变化都是它自己的 KeyFrame
。类似于以下内容:
public void show(ImageView view, Image[] gifs, Duration displayDuration) {
Timeline timeline = new Timeline();
Duration totalDelay = Duration.ZERO;
for (Image gif : gifs) {
KeyFrame frame = new KeyFrame(totalDelay, e -> view.setImage(gif));
timeline.getFrames().add(frame);
totalDelay = totalDelay.add(displayDuration);
}
timeline.getFrames().add(new KeyFrame(totalDelay, e -> view.setImage(null));
timeline.play();
}
这将以 displayDuration
间隔将数组中的 Image
更改为下一个 Image
。
如果您想知道为什么 gif
被允许在 lambda 内部使用——尽管显然被修改了——这是因为我使用了 "enhanced for loop"。增强型 for 循环的变量与常规 for
循环的处理方式不同,因为它在每次迭代时 初始化 。有关详细信息,请参阅