代码之家  ›  专栏  ›  技术社区  ›  Slaw

不确定的ProgressBar在对话框的一部分(JavaFX 10)时不设置动画

  •  10
  • Slaw  · 技术社区  · 8 年前

    当 ProgressBar 是不确定的,它有一个来回的动画。当 进度条 是正常的一部分 Stage 但当 Dialog . 相反,它似乎只是坐在动画的开始这个 进度条 但是,如果设置为某个确定值,则会正确更新。 注意:这个问题在Java8中没有出现 .

    没有任何例外的迹象。

    这是一个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;
            }
    
        }
    
    }
    

    我试过这个密码:

    • JDK 1.8.0_181标准
      • 正常设置动画
    • JDK 10.0.2版
      • 无法设置动画
    • OpenJDK 11 ea+22和openjfx11ea+18
      • 无法设置动画
    • OpenJDK 12个,带OpenJFX 11个,EA+18
      • 无法设置动画
      • 因为使用的是相同的JavaFX版本,所以没想到会有什么变化 是的。

    我所有的测试都是在Windows10家庭电脑(1803版)上进行的。

    我90%肯定这是某种回归错误,但以防万一…

    1. 还有人有这个问题吗?或者我做错了什么,只是侥幸在Java8上工作?
    2. 如果是一个bug,有人知道有什么解决方法吗?

    我试过改变 Modality 和 owner 的 对话 没有效果。似乎只有 进度条 作为 对话 仍然可以移动和消息更新(即用户界面没有冻结)。

    我还没试过其他动画 Node 例如 ProgressIndicator .

    p.s.我在java bug数据库中搜索了这个或类似的问题,但没有找到任何结果;不过,我很容易就漏掉了一个。 .

    更新:我已经提交了自己的bug报告 是的。

    1 回复  |  直到 8 年前
        1
  •  5
  •   Slaw    7 年前

    错误报告可以在以下位置找到: JDK-8207837 .

    • 受影响的版本: 9 我是说, 10 和 openjfx-11 .
    • 受影响的平台:通用(全部)。
    • 固定版本: openjfx-12 .

    原来 Dialog 不是问题的根源该问题是由添加 ProgressBar 给一个 Scene 之后 场景 已添加到 Stage ;造成问题的原因是 JDK-8216377 是的。见 this comment 为了得到更好的解释下面是所附的测试代码,演示了这个问题:

    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) 这个 进度条 将设置动画。

    根据这些信息,我认为在使用 对话 是不可能的一个 对话 有一个内部 舞台 用于显示。当 DialogPane 设置为 对话 它导致了 场景 然后添加到这个 舞台 是的。不幸的是 对话 初始化为 对话框窗格 所以不管 场景 将被添加到 舞台 在添加 进度条 .

    要避免此问题,请使用javafx 8或openjfx 12+。修复程序有可能被(或将被)移植到OpenJFX 11,但我不确定(如果你知道其中一种方法,请留下评论)。