Существует известная проблема в Charm 5.0, когда вы пытаетесь скрыть слой слишком рано .
Когда вы показываете слой, требуется некоторое время, чтобы сделать макет рендеринга, и даже без анимационного перехода, есть промежуток в несколько миллисекунд между временем, когда вы показываете слой, и временем, когда он, наконец, отображается.
Если вы позвоните 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()
не удастся.
Обход
Хотя это исправлено правильно, возможный обходной путь - прослушать событие Layer 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);
}
}
EDIT
Основная проблема связана с тем, как BusyLayer
переопределяет метод Layer::layoutChildren
.
Как вы можете прочитать, прочитайте здесь для Layer::layoutChildren
:
Переопределите этот метод, чтобы добавить логику макета для вашего слоя. Следует позаботиться о том, чтобы вызывать этот метод в переопределенных методах для правильного функционирования Слоя.
Это означает, что вам нужно позвонить super.layoutChildren()
, чтобы правильно настроить слой.
@Override
public void layoutChildren() {
super.layoutChildren();
// add your own implementation
}
Это обычный шаблон, когда расширенные встроенные элементы управления JavaFX.