首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不确定的ProgressBar在对话框的一部分时不动(JavaFX 10)

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

Stack Overflow用户
提问于 2018-07-18 02:43:02
回答 1查看 1.5K关注 0票数 10

当一个ProgressBar是不确定的,它有一个来回的动画。当ProgressBar是普通Stage的一部分时,这很好,但是当Dialog的一部分时就不能工作了。相反,它似乎只是坐在动画的开头。但是,当设置为某个确定值时,ProgressBar确实会正确更新。注意:这个问题在Java 8中没有出现。

没有任何例外的迹象。

下面是一个MCVE (GIF在行动中):

代码语言:javascript
复制
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与OpenJFX 11-ea+18
    • 动不动

  • OpenJDK 12-ea与OpenJFX 11-ea+18
    • 动不动
    • 由于使用的是相同的JavaFX版本,所以并没有真正期望在这里发生更改。

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

我百分之九十肯定这是某种回归错误,但以防万一.

  1. 还有其他人有这个问题吗?还是我做错了什么,它在Java 8上工作是个侥幸呢?
  2. 如果它是一个bug,有人知道任何解决办法吗?

我尝试过将ModalityownerDialog更改为无效。它似乎也仅仅是ProgressBar,因为Dialog仍然可以移动,消息更新(即UI不是冻结的)。

我还没有在其他动画Node(如ProgressIndicator )中尝试过这一点。

我在Java bug数据库中搜索了这个或类似的问题,但没有发现任何问题;但是,我很容易就错过了一个。

更新:我已经提交了自己的错误报告。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-07-19 18:26:15

bug报告可以在这里找到:JDK-8207837

  • 受影响的版本:910openjfx-11
  • 受影响的平台:通用(all)。
  • 固定版本:openjfx-12

事实证明,Dialog并不是问题的根源。这个问题是在ProgressBar已经添加到Stage之后将Scene添加到Stage中引起的;这导致问题的原因是由于JDK-8216377。有关更好的解释,请参见这句话。下面所附的测试代码演示了这个问题:

代码语言:javascript
复制
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将显示动画。

基于这些信息,我认为使用Dialog是不可能的。Dialog有一个用于显示的内部Stage。当在DialogPane上设置一个Dialog时,它会导致创建一个Scene,然后添加到这个Stage中。不幸的是,Dialog是用DialogPane初始化的,因此,在添加ProgressBar之前,Scene将被添加到Stage中。

要避免这个问题,请使用JavaFX 8或OpenJFX 12+。修复可能是(或将要)移植到OpenJFX 11,但我不确定(请留下评论,如果您知道这种或那种方式)。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51392810

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档