Environment: OpenJDK12, JavaFX11
Context:
I have a programm that reads a large document and keeps track of the bytes read by writing the amount to a json file. While reading the file, the ProgressBar
updates showing a percentage.
Not yet implemented, when the programm starts, the ProgressBar
is set according to the bytes read of the file.
Issue:
I read I can't pause a Task because it's the smallest unit of work, it's like one-shot process.
So when I "pause" the Task with service.cancel()
, the progress is still visible.
When I want to resume the reading I have to set the Task to READY
state with service.reset()
, but with this the progress is lost.
How do I manage to not lose the progress?
Tried: I didn't try anything beacuse I don't know what the best approach is. I came up with 2 ideas:
Instead of observe
Task
progress, observe theService
progress, (is it possible?). I mean, in the background,Service
class binds its progress with theTask
progress, they both implementsWorker
interface. So I thought to unbind Service from Task progress and implement its own updateProgress() method? Maybe it's just an overcomplicated idea.Inside createTask(), in task.setOnCancelled() get the progress properties, and set them somehow again to the ProgressBar.
In both cases I don't see how to bind the ProgressBar
progress property with a Service whose Task has been cancelled.
Any help appreciated.
Relevant Code:
Controller:
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();
}
}
Progress cell:
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);
}
}
}
Service:
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: has the buttons to play and pause (cancel) the Task
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);
}
}
}