java - Indeterminate ProgressBar does not animate when part of a Dialog (JavaFX 10)
问题描述
When a ProgressBar
is indeterminate it has an animation that goes back and forth. This works fine when the ProgressBar
is part of a normal Stage
but doesn't work when part of a Dialog
. Instead, it seems to just sit at the start of the animation. The ProgressBar
does, however, update properly when set to some determinate value. Note: The issue does not appear in Java 8.
There are no indications of any exceptions.
Here's an MCVE (GIF of it in action):
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage primaryStage) {
Button detButton = new Button("Launch Determinate Task");
detButton.setOnAction(ae -> {
ae.consume();
createDialog(primaryStage, true)
.showAndWait();
});
Button indetButton = new Button("Launch Indeterminate Task");
indetButton.setOnAction(ae -> {
ae.consume();
createDialog(primaryStage, false)
.showAndWait();
});
HBox btnBox = new HBox(detButton, indetButton);
btnBox.setSpacing(10);
btnBox.setAlignment(Pos.CENTER);
StackPane root = new StackPane(btnBox, createDummyProgressNode());
Scene scene = new Scene(root, 500, 300);
primaryStage.setScene(scene);
primaryStage.setTitle("ProgressBar Issue");
primaryStage.setResizable(false);
primaryStage.show();
}
private Node createDummyProgressNode() {
Label label = new Label("ProgressBar to show animation in Stage.");
ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
VBox box = new VBox(label, progressBar);
box.setMaxHeight(VBox.USE_PREF_SIZE);
box.setSpacing(3);
box.setAlignment(Pos.CENTER_LEFT);
box.setPadding(new Insets(5));
StackPane.setAlignment(box, Pos.BOTTOM_CENTER);
return box;
}
private Dialog<?> createDialog(Stage owner, boolean determinate) {
Task<?> task = new BackgroundTask(determinate);
Dialog<?> dialog = new Dialog<>();
dialog.initOwner(owner);
dialog.setTitle("Background Task - "
+ (determinate ? "Determinate" : "Indeterminate"));
dialog.getDialogPane().setPrefWidth(300);
dialog.getDialogPane().setContent(createDialogContent(task));
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty().bind(task.runningProperty());
dialog.setOnShown(de -> {
de.consume();
executeTask(task);
});
return dialog;
}
private Node createDialogContent(Task<?> task) {
Label label = new Label();
label.textProperty().bind(task.messageProperty());
ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
progressBar.progressProperty().bind(task.progressProperty());
VBox box = new VBox(label, progressBar);
box.setSpacing(3);
box.setAlignment(Pos.CENTER_LEFT);
return box;
}
private void executeTask(Task<?> task) {
Thread thread = new Thread(task, "background-thread");
thread.setDaemon(true);
thread.start();
}
private static class BackgroundTask extends Task<Void> {
private final boolean determinate;
private BackgroundTask(boolean determinate) {
this.determinate = determinate;
}
@Override
protected Void call() throws Exception {
final int loops = 1_000;
for (int i = 0; i <= loops; i++) {
updateMessage("Running... " + i);
Thread.sleep(1L);
if (determinate) {
updateProgress(i, loops);
}
}
updateMessage("Complete");
updateProgress(loops, loops);
return null;
}
}
}
I tried this code on:
- JDK 1.8.0_181
- Animates normally
- JDK 10.0.2
- Fails to animate
- OpenJDK 11-ea+22 with OpenJFX 11-ea+18
- Fails to animate
- OpenJDK 12-ea with OpenJFX 11-ea+18
- Fails to animate
- Didn't really expect a change here since the same JavaFX version is being used.
All my tests were on a Windows 10 Home (version 1803) computer.
I'm 90% certain this is some type of regression bug, but just in case...
- Does anyone else have this problem? Or am I doing something wrong and it's a fluke that it works on Java 8?
- If it is a bug does anyone know of any workarounds?
I've tried things like changing the Modality
and owner
of the Dialog
to no effect. It also seems to only be the ProgressBar
as the Dialog
can still be moved around and the message updates (i.e. the UI is not freezing).
I haven't tried this with other animated Node
s such as ProgressIndicator
.
P.S. I searched the Java bug database for this or similar issue but came up with nothing; I could have easily missed one, however.
Update: I have submitted my own bug report.
解决方案
可以在此处找到错误报告:JDK-8207837。
- 受影响的版本:
9
、10
和openjfx-11
。 - 受影响的平台:通用(全部)。
- 固定版本:
openjfx-12
.
原来这Dialog
不是问题的根源。该问题是由于在已经添加到ProgressBar
aScene
之后添加到 a 引起的;这导致问题的原因是JDK-8216377。有关更多更好的解释,请参阅此评论。这是演示该问题的附加测试代码:Scene
Stage
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IndeterminateProgressBar extends Application {
@Override
public void start(Stage stage) {
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 150);
stage.setScene(scene);
// If the progress bar is added to the root after the root is
// added to the scene and the scene is added to the stage, it will
// fail to be shown.
ProgressBar progBar = new ProgressBar();
progBar.setProgress(-1);
root.getChildren().add(progBar);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
如果你移动到stage.setScene(scene)
后将动画。root.getChildren().add(progBar)
ProgressBar
基于此信息,我相信使用 a 时的解决方法Dialog
是不可能的。ADialog
有一个内部Stage
用于显示。当在 aDialogPane
上设置 a 时,Dialog
它会创建 aScene
然后将其添加到 this Stage
。不幸的是, aDialog
是用 a 初始化的DialogPane
,因此无论Scene
将什么添加到 aStage
之前,都可以添加ProgressBar
.
为避免此问题,请使用 JavaFX 8 或 OpenJFX 12+。该修复程序可能(或将)向后移植到 OpenJFX 11,但我不确定(如果您知道一种或另一种方法,请发表评论)。
推荐阅读
- php - 如何从 WP travel 插件自定义结帐页面
- arrays - 从数组中删除索引号
- list - 如何将参数添加到 haskell 中的列表?
- javascript - Buffer.forEach 在哪里指定?
- python - 何时以及如何从 Spark 中的缓存中删除 DataFrame?
- python - 由于 sklearn 中的未签名二进制文件,公证失败
- android - 如何在 Android 上创建气泡通知?
- php - Laravel 在 url 中显示查询参数
- javascript - 如何使用 async/await 访问 Axios 返回的数据
- flutter - onPressed 不适用于数据库查询