【问题标题】:Detect When A Node is Visible in a Scene检测节点何时在场景中可见
【发布时间】:2017-02-04 15:39:22
【问题描述】:

我正在尝试寻找一种方法来检测(或接收通知)节点已添加到场景中并且可见。

我在 JavaFx 主线程之外创建 Node 对象,并使用 Platform.runLater() 将它们添加到舞台和场景中。但是我希望 Node 对象接收已添加到场景中并且可见的通知,例如我希望触发动画开始。

我似乎找不到任何属性或方法来添加侦听器来捕获此类事件。有什么建议吗?

【问题讨论】:

    标签: javafx javafx-8


    【解决方案1】:

    第三方 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)-&gt;{ if (newValue) { TO DO WHEN NODE IS VISIBLE}});。但是,在我看来,我得到了这个事件,但节点还不可见。是真的吗?
    • @Pavel_K 据我所知没有。您可以通过minimal reproducible example 发布问题。
    • 这个解决方案给出了一个问题的答案 - How to detect when a node becomes a part of scene? 但不是一个问题 How to detect when a node becomes visible?
    • @Pavel_K 不,这不是真的。如果节点是场景的一部分,则showing 属性不会变为真,除非场景是窗口的一部分并且窗口正在显示,这是对“可见”的合理描述,当然也适用于这个问题。从您发布的其他内容中,您想知道“何时执行布局计算(或何时执行布局计算)”?这也许是“可见”的另一个合理定义,但出于这些目的,您应该使用适当的布局技术。
    【解决方案2】:

    您可以将侦听器添加到要向其中添加新节点的容器节点的 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);
    

    【讨论】:

    • 是否没有从 JavaFX 本身到节点的通知/事件?
    • JavaFX 节点添加到容器时不会发生任何变化
    • @Buddha 最后一条评论中的说法不正确:节点有一个parentProperty(),当节点添加到容器时它会改变。此外,sceneProperty() 可能会发生变化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-09
    • 2015-04-28
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多