服务任务完成后如何保持 ProgressBar 进度?

How to keep ProgressBar progress when Service Task finish?

环境:OpenJDK12、JavaFX11

上下文: 我有一个程序可以读取大型文档并通过将数量写入 json 文件来跟踪读取的字节数。读取文件时,ProgressBar 更新显示百分比。 尚未实现,当程序启动时,ProgressBar根据读取文件的字节数设置。

问题: 我读到我无法暂停任务,因为它是最小的工作单元,就像一次性过程。 因此,当我 "pause" 任务 service.cancel() 时,进度仍然可见。 当我想恢复阅读时,我必须使用 service.reset() 将任务设置为 READY 状态,但是这样进度就会丢失。

如何才能不丢失进度?

尝试过: 我没有尝试任何东西,因为我不知道最好的方法是什么。我想到了 2 个想法:

  1. 而不是观察 Task 进度,而是观察 Service 进度,(这可能吗?)。我的意思是,在后台,Service class 将其进度与 Task 进度绑定,它们都实现了 Worker 接口。 所以我想解除 Service 与 Task progress 的绑定并实现它自己的 updateProgress() 方法?可能是 这只是一个过于复杂的想法。

  2. 在 createTask() 中,在 task.setOnCancelled() 中获取进度属性,并以某种方式再次将它们设置到 ProgressBar。

在这两种情况下,我都看不到如何将 ProgressBar 进度 属性 与任务已取消的服务绑定。

感谢任何帮助。

相关代码:

控制器:

public class MyController implements Initializable{
    @FXML
    private TableView<MyService> dataTable;

    @FXML
    private TableColumn<MyService, Double> progressColumn;
    @FXML
    private TableColumn<MyService,Object> actionColumn;

    private ObservableList<MyService> observableList;
    private List<MyService> serviceList;
    private MyThreadPoolExecutor threadPoolExecutor;  

    public MyController(){
      threadPoolExecutor = new MyThreadPoolExecutor();
      List<MyBean> myFileList ;//get files...

      myFileList.forEach(f->{
        serviceList.add(new MyService(f,threadPoolExecutor));
      });
      observableList = FXCollections.observableArrayList(serviceList);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        progressColumn.setCellValueFactory(new PropertyValueFactory<MyService,Double>("progress"));
        progressColumn.setCellFactory(ProgressCell.<Double>forTableColumn());
        actionColumn.setCellValueFactory(new PropertyValueFactory<MyService,Object> "taskAction"));
        actionColumn.setCellFactory(ActionCell.<Object>forTableColumn(this));

        dataTable.setItems(observableList);
    }

    public void startReading(MyService service) {
      service.start();  
    }
    public void pauseReading(MyService service){
      service.cancel();
      service.reset();
    }

}

进度单元格:

  public class ProgressCell extends TableCell<MyService , Double> {

    private ProgressBar bar;
    private ObservableValue<Double> observable;


    public static <S>Callback<TableColumn<MyService ,Double>,TableCell<MyService ,Double>>
    forTableColumn() {
        return param -> new ProgressCell();
    }

    @Override
    protected void updateItem(Double item, boolean empty) {

        super.updateItem(item, empty);

        if (empty || item == null) {
            setGraphic(null);
        } else {
          TableColumn<MyService , Double> column = getTableColumn();
          observable = column == null ? null : column.getCellObservableValue(getIndex());

            if (observable != null) {
                bar.progressProperty().bind(observable);
            } else if (item != null) {
                bar.setProgress(item);
            }
            setGraphic(bar);
       }

    }

}

服务:

public class MyService extends Service<Double>{
    private SimpleObjectProperty<MyBean> file;
    private SimpleObjectProperty<Object> taskAction;

    public MyService(MyBean file, ThreadPoolExecutor executor) {
        this.file= new SimpleObjectProperty<MyBean>(file);
        super.setExecutor(executor);
    }

    @Override
    protected Task createTask() {
      try{
        MyTask task = new MyTask(this.file);
        task.setOnCancelled(event->{
          //get progress properties to save or something
        });
          return task;
      }catch(IOException e){
         e.printStacktrace();
      }
     return null;
    }

  //getters , setters & properties
}

ActionCell:具有播放和暂停(取消)任务的按钮

public ActionCell extends TableCell<MyService, Object>{
    private HBox hbox;
    private Button playButton;
    private Button pauseButton;

    public ActionCell(MyController cont){
      hbox = new HBox();
      playButton= new Button();
      pauseButton= new Button();

      playButton.setOnAction(event->{ 
            MyService service = getTableView().getItems().get(getIndex());
            cont.startReading(service);
            playButton.setDisable(true);
            pauseButton.setDisable(false);
        });

      pauseButton.setOnAction(event->{
            MyService service = getTableView().getItems().get(getIndex());
            cont.pauseReading(service); 
            playButton.setDisable(false);
            pauseButton.setDisable(true);
    });
    hbox.getChildren().addAll(playButton, pauseButton);

    }

    public static <S> Callback<TableColumn<MyService, Object>, TableCell<MyService, Object>>forTableColumn(MyController cont){
        return param -> new ActionCell(cont)
    }

    @Override
    protected void updateItem(Object item, boolean empty) {
       super.updateItem(item, empty);
    if (empty || item == null) {
        setGraphic(null);
    } else {            
        setGraphic(hbox);
    }
    }
} 

由于我没有找到有用的设计方法,也没有找到任何具有多个服务和线程执行器的可暂停任务示例,所以我自己找到了解决方案。它不完美,但要么是错误的。

问题出在按下暂停键时取消和重置任务。这导致 Task 不再存在,并且 Worker 状态更改为 READY 并因此失去进度。

解决方案是在 start() 之前的 reset() 上,仅在需要时(如果任务被取消)。因此,当任务重新启动时,它会自行恢复进度并将其再次设置为 ProgressBar

public void startReading(MyService service) {
  //start task and reset only if needed
  if(service.getState().equals(Worker.State.CANCELLED)) {
     service.reset();
     service.start();
  }else {
     service.start();
  }

}

public void pauseReading(MyService service) {
    service.cancel();
}

参考文献:

Service

Worker

Task