当前位置: 代码迷 >> java >> ObserverPattern,Platfrom.runLater()和ProgressBar:复制大量文件时无法正常工作
  详细解决方案

ObserverPattern,Platfrom.runLater()和ProgressBar:复制大量文件时无法正常工作

热度:44   发布时间:2023-07-31 13:31:56.0

我制作了一个小型的MVC程序,该程序将文件从A复制到B。为了更好地显示进度,我使用ProgressBarObserver Pattern进行刷新。

模型(复印机和可观察的):

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

和视图(ProgressBar +观察者):

[...]
    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 Thread却没有时间做它的工作。

整个问题使我想到了代码,并且我怀疑这种用观察者模式快速更新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事物和关键字(进度,完成,事件)的这种方式感到怀疑。 好吧,它大多数时候都可以使用这种方式工作,但这是否是一种好方法?

非常感谢你!

我怀疑您的问题是由于您传递给progressBar.setProgress的值所致。 该值必须介于0.0到1.0之间,但看起来您可能正在传递复制的文件数。

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

尤其是JavaFX,使用可观察的,可绑定的属性。 您的progress变量可以替换为 ,其可观察的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();
  相关解决方案