【发布时间】:2017-02-04 15:39:22
【问题描述】:
我正在尝试寻找一种方法来检测(或接收通知)节点已添加到场景中并且可见。
我在 JavaFx 主线程之外创建 Node 对象,并使用 Platform.runLater() 将它们添加到舞台和场景中。但是我希望 Node 对象接收已添加到场景中并且可见的通知,例如我希望触发动画开始。
我似乎找不到任何属性或方法来添加侦听器来捕获此类事件。有什么建议吗?
【问题讨论】:
我正在尝试寻找一种方法来检测(或接收通知)节点已添加到场景中并且可见。
我在 JavaFx 主线程之外创建 Node 对象,并使用 Platform.runLater() 将它们添加到舞台和场景中。但是我希望 Node 对象接收已添加到场景中并且可见的通知,例如我希望触发动画开始。
我似乎找不到任何属性或方法来添加侦听器来捕获此类事件。有什么建议吗?
【问题讨论】:
第三方 JavaFX 库 ReactFX 对此有一个机制,blog 中引用了这个确切的用例。总之,你可以这样做
Val<Boolean> showing = Val.flatMap(node.sceneProperty(), Scene::windowProperty)
.flatMap(Window::showingProperty);
当然还有
showing.addListener((obs, wasShowing, isNowShowing) -> {
if (isNowShowing) {
// node is showing
} else {
// node is not showing
}
});
标准库有这个版本,但写得很糟糕。 (它不是类型安全的,没有编译时检查属性是否存在,并且如果“链”中的任何属性为空,也会通过管道向标准错误发出许多不必要的警告,即使 API 文档表明这是支持的用例。)如果您想使用标准 JavaFX 库执行此操作,您可以这样做
BooleanBinding showing = Bindings.selectBoolean(node.sceneProperty(), "window", "showing");
然后以与上述相同的方式使用绑定。
最后,您可以手动完成所有这些操作,但是正确管理侦听器有点难看:
BooleanProperty showing = new SimpleBooleanProperty();
ChangeListener<Window> windowListener = (obs, oldWindow, newWindow) -> {
showing.unbind();
if (newWindow != null) {
showing.bind(newWindow.showingProperty());
} else {
showing.set(false);
}
};
ChangeListener sceneListener = (obs, oldScene, newScene) -> {
showing.unbind();
if (oldScene != null) {
oldScene.windowProperty().removeListener(windowListener);
}
if (newScene == null) {
showing.set(false);
} else {
newScene.windowProperty().addListener(windowListener);
if (newScene.getWindow() == null) {
showing.set(false);
} else {
showing.bind(newScene.getWindow().showingProperty());
}
}
};
node.sceneProperty().addListener(sceneListener);
if (node.getScene() == null) {
showing.set(false);
} else {
node.getScene().windowProperty().add(windowListener);
if (node.getScene().getWindow() == null) {
showing.set(false);
} else {
showing.bind(node.getScene().getWindow().showingProperty());
}
}
【讨论】:
Finally, you could do all this by hand, but it gets a bit ugly to manage the listeners properly 之后使用您的代码。我做showing.addListener((observable, oldValue, newValue)->{ if (newValue) { TO DO WHEN NODE IS VISIBLE}});。但是,在我看来,我得到了这个事件,但节点还不可见。是真的吗?
How to detect when a node becomes a part of scene? 但不是一个问题 How to detect when a node becomes visible?
showing 属性不会变为真,除非场景是窗口的一部分并且窗口正在显示,这是对“可见”的合理描述,当然也适用于这个问题。从您发布的其他内容中,您想知道“何时执行布局计算(或何时执行布局计算)”?这也许是“可见”的另一个合理定义,但出于这些目的,您应该使用适当的布局技术。
您可以将侦听器添加到要向其中添加新节点的容器节点的 children 属性。
grid.getChildren().addListener((ListChangeListener<? super Node>) change -> {
System.out.println(change.getList().get(0).getTypeSelector());
});
change.getList().get(0) 返回添加到网格对象的第一个节点。
在詹姆斯的评论之后,我查了一下,是的,从节点的角度来看也是可能的。可以在节点上监听 parentProeprty 的变化。以下 sn-p 显示了执行此操作的方法。
Button b = new Button("Test");
b.parentProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("added to a container " + newValue);
});
answerPane.getChildren().add(b);
【讨论】:
parentProperty(),当节点添加到容器时它会改变。此外,sceneProperty() 可能会发生变化。