I've created a busy layer showing an animating progress indicator while background IO is busy. The layer is added to the glasspane in Gluon mobile 4:
BusyLayer extends Layer { ...
root = new FlowPane(new ProgressIndicator());
MobileApplication.getInstance().getGlassPane().getLayers().add(this);
DH2FX extends MobileApplication { ...
addLayerFactory("Busy", () -> new BusyLayer());
...
showLayer("Busy");
...
hideLayer("Busy");
In Gluon 5 getLayers has been removed and according to the migration guide layers can be shown directly:
BusyLayer extends Layer { ...
root = new FlowPane(new ProgressIndicator());
DH2FX extends MobileApplication { ...
BusyLayer busyLayer = new BusyLayer();
...
busyLayer.show();
...
busyLayer.hide();
But the layer is not hidden.
====
The main players are a singleton Background class, so the BusyLayer is only shown once:
class BackgroundActivity {
private final AtomicInteger busyAtomicInteger = new AtomicInteger(0);
BusyLayer busyLayer = new BusyLayer();
private long time;
public BackgroundActivity() {
busyLayer.setOnShowing(e -> {
time = System.currentTimeMillis();
System.out.println("Showing busyLayer");
});
busyLayer.setOnShown(e -> {
System.out.println("busyLayer shown in: " + (System.currentTimeMillis() - time) + " ms");
});
busyLayer.setOnHiding(e -> System.out.println("hiding layer at " + (System.currentTimeMillis() - time) + " ms"));
}
void start() {
if (busyAtomicInteger.getAndIncrement() == 0) {
busyLayer.show();
}
}
void done() {
if (busyAtomicInteger.decrementAndGet() == 0) {
busyLayer.hide();
}
}
void failure(Throwable t) {
t.printStackTrace();
failure();
}
void failure() {
done();
}
}
protected final BackgroundActivity backgroundActivity = new BackgroundActivity();
And code like this using CompletableFutures to do asynchronous tasks:
// Hours
backgroundActivity.start();
CompletableFuture.supplyAsync( () -> entryService().getHours(calendarPickerForHessian))
.exceptionally( e -> { backgroundActivity.failure(e); return null; } )
.thenAcceptAsync( (Hour[] hours) -> {
Platform.runLater( () -> {
refreshHours(hours);
backgroundActivity.done();
});
});
// ProjectTotals
backgroundActivity.start();
CompletableFuture.supplyAsync( () -> entryService().getProjectTotals(calendarPickerForHessian) )
.exceptionally( e -> { backgroundActivity.failure(e); return null; } )
.thenAcceptAsync( (LinkedHashMap<Integer, Double> projectTotals) -> {
Platform.runLater( () -> {
refreshProjectTotals(projectTotals);
backgroundActivity.done();
});
});
// DayTotals
backgroundActivity.start();
CompletableFuture.supplyAsync( () -> entryService().getDayTotals(calendarPickerForHessian))
.exceptionally( e -> { backgroundActivity.failure(e); return null; } )
.thenAcceptAsync( (SortedMap<String, Double> dayTotals) -> {
Platform.runLater( () -> {
refreshDayTotals(dayTotals);
backgroundActivity.done();
});
});
And ofcourse BusyLayer itself:
public class BusyLayer extends Layer {
public BusyLayer() {
root = new StackPane(new ProgressIndicator());
root.setAlignment(Pos.CENTER);
root.getStyleClass().add("semitransparent7");
getChildren().add(root);
}
private final StackPane root;
@Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
GlassPane glassPane = MobileApplication.getInstance().getGlassPane();
root.resize(glassPane.getWidth(), glassPane.getHeight());
resizeRelocate(0, 0, glassPane.getWidth(), glassPane.getHeight());
}
}