【问题标题】:ObserverPattern, Platfrom.runLater() and ProgressBar: Not working properly when copying a large amount of filesObserverPattern、Platfrom.runLater() 和 ProgressBar:复制大量文件时无法正常工作
【发布时间】:2015-07-20 00:44:01
【问题描述】:

我制作了一个小型 MVC 程序,将文件从 A 复制到 B。为了更好地指示进度,我使用 ProgressBar 和 Observer Pattern 来刷新它。

模型(复制器和 Observable):

[...]
    Files.copy(file.toPath(), Paths.get(destinationPath.toString() + "/" + file.getName()),
        StandardCopyOption.REPLACE_EXISTING);
    copyingFile = file.getName();
    progress++;
    setChanged();
    notifyObservers("progress");
[...]

还有视图(ProgressBar + Observer):

[...]
    progressBar = new ProgressBar();
[...]
    @Override
    public void update(Observable observable, Object arg) {
        if (observable != null && observable instanceof MainModel) {
            if (arg instanceof String && "progress".equals(arg)) {
                Platform.runLater(() -> {
                    progressBar.setProgress(model.getProgress());
                    copyingFile.setText(model.getCopyingFile());
                    percent.setText(df.format(progressBar.getProgress()* 100) + "%");
                });
         }
[...]

这很好用。 ProgressBar 不断更新,但如果我复制 100 多个文件(大约 2MB),它就不再工作了。

似乎notifyObservers("progress") 经常触发并卡住,或者线程太忙于复制文件,应该更新ProgressBar 的JavaFX 线程没有时间做它的工作。

整个问题让我想到了代码,我怀疑这种使用观察者模式快速更新ProgressBar 的方法似乎不是一个好方法。

更新ProgressBar 的正确方法是什么?绑定属性?

补充问题: 我正在使用观察者模式来更新视图中的所有数据。因为模型需要更新很多不同的东西,而且它并不总是需要更新整个数据,所以我使用这种更新方式:

@Override
    public void update(Observable observable, Object arg) {
        if (observable != null && observable instanceof MainModel) {

        if (arg instanceof String && "progress".equals(arg)) {
            Platform.runLater(() -> {
                progressBar.setProgress(model.getProgress());
                copyingFile.setText(model.getCopyingFile());
                percent.setText(df.format(progressBar.getProgress() * 100) + "%");

                if (progressBar.getProgress() > 0.5) {
                    percent.setTextFill(Color.WHITE);
                }

            });

        } else if (arg instanceof String && "finished".equals(arg)) {
            Platform.runLater(() -> {
                progressBar.setProgress(1);
                copyingFile.setText("Fertig!");
                percent.setText("100%");
                finishButton.setDisable(false);
            });

        } else if (arg instanceof String && "events".equals(arg)) {

            eventChoiceBox.setDisable(false);
            List<String> events = new LinkedList<>();
            events.addAll(model.getEvents());
            Collections.sort(events);
            events.add(0, "Neue Veranstaltung...");
            eventChoiceBox.setItems(FXCollections.observableArrayList(events));
            eventChoiceBox.getSelectionModel().select(model.getEvent());

        } else {

            userNames.setItems(FXCollections.observableArrayList(model.getUsers()));
            filesList.setItems(FXCollections.observableArrayList(model.getFilesAsString()));
            userNames.getSelectionModel().select(model.getUser());

        }
    }

我对整个if-else 事物和关键字(进度、完成、事件)的这种方式持怀疑态度。 好吧,它在大多数情况下都有效并使用这种方式,但这甚至是一种好方法吗?

非常感谢!

【问题讨论】:

  • 你能比“它不再工作”更具体吗?当您复制 100 个或更多文件时,您看到了什么行为?

标签: java javafx progress-bar observer-pattern


【解决方案1】:

我怀疑您的问题是由于您传递给progressBar.setProgress 的值。该值需要介于 0.0 和 1.0 之间,但看起来您可能传递了复制的文件数。

有很多方法可以实现观察者模式,但实际上并不使用 Observable 和 Observer 类。

JavaFX 尤其使用可观察的、可绑定的属性。您的progress 变量可以替换为Task,其可观察的progressProperty 可以绑定到您的ProgressBar 的值。也可以绑定百分比指标,任务的message属性可以绑定当前文件指标:

Task<?> copyTask = new Task<Void>() {
    @Override
    protected Void call()
    throws IOException {
        int progress = 0;
        for (File file : files) {
            Files.copy(file.toPath(), Paths.get(destinationPath.toString(), file.getName()),
                StandardCopyOption.REPLACE_EXISTING);
            updateMessage(file.getName());
            updateProgress(++progress, files.size());
        }
        return null;
    }
};

ReadOnlyDoubleProperty taskProgress = copyTask.progressProperty();

progressBar.progressProperty().bind(taskProgress);
finishButton.disableProperty().bind(taskProgress.lessThan(1.0));
percent.textProperty().bind(
    taskProgress.multiply(100).asString("%.0f%%"));

Paint defaultColor = new Label().getTextFill();
Paint halfDoneColor = Color.WHITE;
percent.textFillProperty().bind(
    Bindings.when(taskProgress.greaterThan(0.5)).then(halfDoneColor).otherwise(defaultColor));

copyingFile.textProperty().bind(copyTask.messageProperty());

new Thread(copyTask).start();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多