JavaFX UI 冻结问题

5
我正在尝试展示每个任务的进度和状态。每个任务都在TableView中以一行表示。每个任务在不同的Thread中并行执行。请参考TableView图像。
对于"Progress" TableColumn,我已经设置了单元格工厂来呈现`ProgressBar"。
  public static class ProgressBarTableCell<S, T> extends TableCell<S, T> {

        private final ProgressBar progressBar;
        private ObservableValue<T> ov;

        public ProgressBarTableCell() {
            this.progressBar = new ProgressBar();
            progressBar.setPrefHeight(23);
            setAlignment(Pos.CENTER);
        }

        @Override
        public void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null) {
                setGraphic(null);
                setText(null);
            } else {
                if (item.toString().equalsIgnoreCase("Processing")) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {

                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(-1);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(-1);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                } else {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(0);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(0);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                }
            }
        }
    } 

TableView没有显示任务的最新状态和进度,所以我在后台定期运行一个线程,当我调用startUpdatingTableProgress()时,修改TableView并停止运行时调用stopUpdatingTableProgress()

 public void stopUpdatingTableProgress() {
    keepUpdating = false;
}

public void startUpdatingTableProgress() {
    keepUpdating = true;
    TableProgressBarUpdator tpu = new TableProgressBarUpdator(table);
    tpu.start();
}

class TableProgressBarUpdator implements Runnable {

    TableView table;

    public TableProgressBarUpdator(TableView fxtable) {
        table = fxtable;

    }

    public void start() {
        new Thread(this).start();
    }

    public void run() {

        while (keepUpdating) {
            try {
                updateProgressbar();
                Thread.sleep(1000);
            } catch (Exception e) {
                LogHandler.doErrorLogging("Error while updating tables cell", e);
            }
        }
        LogHandler.doDebugLogging("Table process repainting is completed.");
    }

    private void updateProgressbar() throws Exception {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                ((TableColumn) table.getColumns().get(0)).setVisible(false);
                ((TableColumn) table.getColumns().get(0)).setVisible(true);
            }
        });


    }
}

但是我的JavaFX应用程序在连续使用后会冻结UI。有没有其他方法可以在后台线程中自动触发特定时间间隔后调用updateProgressbar(),并且不会冻结我的JavaFX应用程序的UI?
我不知道是否正确更新每个任务在单独线程中运行的任务状态。如果我想借助javafx.concurrent包来实现这一点,那么有人能给我一些指导或流程吗?任务将放入什么位置,服务类将放入什么位置?我如何安排它以更新进度条?FYI:一次只允许执行5个线程。
编辑 -
根据Andy Till先生的建议,我已经更新了我的代码,但仍然没有运气。
TableColumn tableColumn_Progress = new TableColumn("Progress");
        tableColumn_Progress.setCellValueFactory(new PropertyValueFactory<QueueData, Double>("progressBar"));
        tableColumn_Progress.setPrefWidth(100);

        Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>> attachmentCellFactory = //
                new Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>>() {
            @Override
            public TableCell call(final TableColumn param) {
                final ProgressBarTableCell cell = new ProgressBarTableCell() {
                    @Override
                    public void updateItem(Object item, boolean empty) {
                        super.updateItem(item, empty);
                    }
                };
                return cell;
            }
        };
        tableColumn_Progress.setCellFactory(attachmentCellFactory);

在 QueueData 类中 -
DoubleProperty progressBar = null;

public DoubleProperty progressBarProperty() {
        return progressBar;
    }

    public Double getprogressBar() {
        return progressBar.get();
    }

    public void setprogressBar(Double sType) {
        this.progressBar = new SimpleDoubleProperty(sType);
    }

我尝试过不使用后台线程更新UI,但没有成功。

2个回答

5
您的“QueueData”类应该有设置现有进度双精度属性的方法,似乎您在setprogressBar(Double sType)方法中每次设置进度状态时都创建了新的SimpleDoubleProperty。

请将其更新为 -

 public void setprogressBar(Double sType) {
        this.progressBar.set(sType);
    }

在开始时只需创建一个进度条对象,然后更新现有的对象即可。


5
据我了解,您的代码是翻转可见性以触发更新表格单元格的updateItem方法?这意味着每秒都可能有任意数量的线程尝试更改可见性,并且不停地重绘。
虽然我知道您已经非常了解实现,但JavaFX服务类已经实现了进度功能,您可以更新它,这比您现在所做的要简单得多。
最好拥有一个进度double属性,您从线程中更新该属性并将其绑定到ProgressBar.progressProperty(),这样一切都会自动更新。这就是Service类的工作方式,但易于自己实现。
此外,请确保释放您的线程,这很容易检查。在Eclipse中进行调试,在调试透视图中,它将向您显示线程列表,请确保其不会无限增长。
要安排运行Runnable,请检查ScheduledExecutor类。您可以使用Executors.newScheduledThreadPool(numOfThreads)轻松创建它。

谢谢。我根据你的建议更新了我的代码,但是没有起作用。我已经编辑了我的问题并添加了我所做的代码。 - Ashish Pancholi
请问您能否给我展示一下如何使用ProgressBar.progressProperty()以及在哪里使用它的例子?谢谢。 - Ashish Pancholi
我不知道我是否走在正确的路上。如果我想要使用javafx.concurrent包来实现这个,那么我该怎么做呢? - Ashish Pancholi
任务(Task)和服务类(Service Class)中会有什么内容? - Ashish Pancholi
progressBar.progressProperty().bind(myService.progressProperty())应该将服务进度绑定到进度条上,并在从服务更新Service.progressProperty()时自动更新它。网络上有一些不错的 Service 教程。 - Andy Till

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接