[英]Gluon mobile 5 layer does not hide
我創建了一個繁忙的層,在后台 IO 繁忙時顯示一個動畫進度指示器。 該層被添加到 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");
在 Gluon 5 中 getLayers 已被刪除,根據遷移指南,可以直接顯示圖層:
BusyLayer extends Layer { ...
root = new FlowPane(new ProgressIndicator());
DH2FX extends MobileApplication { ...
BusyLayer busyLayer = new BusyLayer();
...
busyLayer.show();
...
busyLayer.hide();
但該層並未隱藏。
====
主要播放器是一個單例 Background 類,因此 BusyLayer 只顯示一次:
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();
使用 CompletableFutures 執行異步任務的代碼如下:
// 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();
});
});
當然還有 BusyLayer 本身:
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());
}
}
當您嘗試過早隱藏圖層時,Charm 5.0 存在一個已知問題。
當您顯示一個圖層時,需要一些時間來進行渲染布局,即使沒有動畫過渡,您顯示圖層的時間和最終顯示的時間之間也有幾毫秒的差距。
如果在顯示Layer::hide
之前調用Layer::hide
,該調用將退出,並且該圖層不會被隱藏。
一個簡單的測試如下:
private long time;
BusyLayer busyLayer = new BusyLayer();
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"));
busyLayer.show();
現在假設你有一個需要一秒鍾的長任務:
PauseTransition p = new PauseTransition(Duration.seconds(1));
p.setOnFinished(f -> busyLayer.hide());
p.play();
那么該層將按預期隱藏。
但是如果任務速度更快並且需要幾毫秒:
PauseTransition p = new PauseTransition(Duration.seconds(0.01));
p.setOnFinished(f -> busyLayer.hide());
p.play();
該層可能尚未顯示,且hide()
調用將失敗。
解決方法
雖然這是正確修復的,但可能的解決方法是偵聽圖層的LifecycleEvent.SHOWN
事件,並執行以下操作:
private BooleanProperty shown = new SimpleBooleanProperty();
BusyLayer busyLayer = new BusyLayer();
busyLayer.setOnShowing(e -> shown.set(false));
busyLayer.setOnShown(e -> shown.set(true));
busyLayer.show();
PauseTransition p = new PauseTransition(taskDuration);
p.setOnFinished(f -> {
if (shown.get()) {
// layer was shown, hide it
busyLayer.hide();
} else {
// layer is not shown yet, wait until it does, and hide
shown.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (shown.get()) {
busyLayer.hide();
shown.removeListener(this);
}
}
});
}
});
p.play();
編輯
我正在添加一個可能的BusyLayer
實現:
class BusyLayer extends Layer {
private final GlassPane glassPane = MobileApplication.getInstance().getGlassPane();
private final StackPane root;
private final double size = 150;
public BusyLayer() {
root = new StackPane(new ProgressIndicator());
root.setStyle("-fx-background-color: white;");
getChildren().add(root);
setBackgroundFade(0.5);
}
@Override
public void layoutChildren() {
super.layoutChildren();
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(size, size);
resizeRelocate((glassPane.getWidth() - size)/2, (glassPane.getHeight()- size)/2, size, size);
}
}
編輯
主要問題與BusyLayer
如何覆蓋Layer::layoutChildren
方法有關。
正如您可以在此處閱讀Layer::layoutChildren
:
覆蓋此方法為您的圖層添加布局邏輯。 應注意在覆蓋方法中調用此方法以使層正常運行。
這意味着您必須調用super.layoutChildren()
才能使圖層正常工作。
@Override
public void layoutChildren() {
super.layoutChildren();
// add your own implementation
}
這是擴展 JavaFX 內置控件時的常見模式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.