服务任务完成后如何保持 ProgressBar 进度?
How to keep ProgressBar progress when Service Task finish?
环境:OpenJDK12、JavaFX11
上下文:
我有一个程序可以读取大型文档并通过将数量写入 json 文件来跟踪读取的字节数。读取文件时,ProgressBar
更新显示百分比。
尚未实现,当程序启动时,ProgressBar
根据读取文件的字节数设置。
问题:
我读到我无法暂停任务,因为它是最小的工作单元,就像一次性过程。
因此,当我 "pause" 任务 service.cancel()
时,进度仍然可见。
当我想恢复阅读时,我必须使用 service.reset()
将任务设置为 READY
状态,但是这样进度就会丢失。
如何才能不丢失进度?
尝试过:
我没有尝试任何东西,因为我不知道最好的方法是什么。我想到了 2 个想法:
而不是观察 Task
进度,而是观察 Service
进度,(这可能吗?)。我的意思是,在后台,Service
class 将其进度与 Task
进度绑定,它们都实现了 Worker
接口。
所以我想解除 Service 与 Task progress 的绑定并实现它自己的 updateProgress() 方法?可能是
这只是一个过于复杂的想法。
在 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();
}
参考文献:
环境:OpenJDK12、JavaFX11
上下文:
我有一个程序可以读取大型文档并通过将数量写入 json 文件来跟踪读取的字节数。读取文件时,ProgressBar
更新显示百分比。
尚未实现,当程序启动时,ProgressBar
根据读取文件的字节数设置。
问题:
我读到我无法暂停任务,因为它是最小的工作单元,就像一次性过程。
因此,当我 "pause" 任务 service.cancel()
时,进度仍然可见。
当我想恢复阅读时,我必须使用 service.reset()
将任务设置为 READY
状态,但是这样进度就会丢失。
如何才能不丢失进度?
尝试过: 我没有尝试任何东西,因为我不知道最好的方法是什么。我想到了 2 个想法:
而不是观察
Task
进度,而是观察Service
进度,(这可能吗?)。我的意思是,在后台,Service
class 将其进度与Task
进度绑定,它们都实现了Worker
接口。 所以我想解除 Service 与 Task progress 的绑定并实现它自己的 updateProgress() 方法?可能是 这只是一个过于复杂的想法。在 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();
}
参考文献: