获取JavaFX8中节点的可见状态

获取JavaFX8中节点的可见状态,java,javafx,javafx-8,Java,Javafx,Javafx 8,我需要检测节点当前是否正在显示。 也就是说,如果我的节点在选项卡窗格中,我需要知道它是否在所选选项卡中 在本例中,我想知道HBox何时显示。节点的visibleProperty和managedProperty似乎对我没有帮助: public class VisibleTest extends Application { @Override public void start(Stage primaryStage) throws Exception { TabPane tabpane

我需要检测节点当前是否正在显示。 也就是说,如果我的节点在选项卡窗格中,我需要知道它是否在所选选项卡中

在本例中,我想知道HBox何时显示。节点的visibleProperty和managedProperty似乎对我没有帮助:

public class VisibleTest extends Application {

@Override
public void start(Stage primaryStage) throws Exception {

    TabPane tabpane = new TabPane();
    tabpane.getTabs().add(new Tab("Tab1", new Label("Label1")));

    HBox hbox = new HBox(new Label("Label2"));
    hbox.setStyle("-fx-background-color: aquamarine;");

    hbox.visibleProperty().addListener((observable, oldValue, newValue) -> {
        System.out.println("Hbox visible changed. newValue: " + newValue);
    });

    hbox.managedProperty().addListener((observable, oldValue, newValue) -> {
        System.out.println("Hbox managed changed. newValue: " + newValue);
    });

    Tab tab2 = new Tab("tab2", hbox);
    tabpane.getTabs().add(tab2);

    primaryStage.setScene(new Scene(tabpane));
    primaryStage.setWidth(600);
    primaryStage.setHeight(500);
    primaryStage.show();
}

public static void main(String[] args) {
    launch(args);
}
}
我知道,可以监听选项卡的
selectedProperty
状态,但这并不能解决我真正的问题

Node.impl\u isTreeVisible()
实现了我想要的功能,但这是一个无润滑的API

有什么想法吗

------------------------------------更新-----------------
我意识到上面的代码示例并不能很好地解释我要实现的目标。
下面是一些Swing代码,它演示了我在JavaFX中要实现的目标。检测JComponent/节点是否可见/显示,并基于该状态启动或停止后台进程。如果它是一个javaFX类,构造函数会是什么样子

public class SwingVisible extends JComponent {

    String instanceNR;
    Thread instanceThread;
    boolean doExpensiveStuff = false;

    public SwingVisible(String instanceNR) {
        this.instanceNR = instanceNR;
        this.setLayout(new FlowLayout());
        this.add(new JLabel(instanceNR));

        instanceThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (doExpensiveStuff) {
                        /*
                         * do expensive stuff.
                         */
                        System.out.println(instanceNR + " is visible " + isVisible());
                    }
                }
            }
        });

        /*
         * How to do this in FX?
         */
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentShown(ComponentEvent e) {
                if (!instanceThread.isAlive()) {
                    instanceThread.start();
                }
                doExpensiveStuff = true;
            }

            @Override
            public void componentHidden(ComponentEvent e) {
                doExpensiveStuff = false;
            }
        });
    }

    public static void main(String[] args) {    
        /*
         * This block represents code that is external to my library. End user
         * can put instances of SwingVisible in JTabbedPanes, JFrames, JWindows,
         * or other JComponents. How many instances there will bee is not in my
         * control.
         */
        JTabbedPane jtp = new JTabbedPane();
        jtp.add("tab1", new SwingVisible("1"));
        jtp.add("tab2", new SwingVisible("2"));
        jtp.add("tab3", new SwingVisible("3"));

        JFrame f = new JFrame("test");
        f.setContentPane(jtp);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(300, 300);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
选择tab1时的输出:

1是可见的真值
1是可见的真值
1是可见的真值

选择tab2时的输出:

2是可见的真值
2是可见的真值
2是可见的真值

更新答案

tab2.getContent().isVisible();

在我看来,我最初的答案是正确的。如果没有,你需要以更好的方式提问。您想知道hbox何时可见(意味着您可以在屏幕上看到hbox)


您可以使用
选项卡
,了解它是否被选中,并扩展其内容是否可见。它是一个布尔属性

我已经根据您最初的JavaFX示例将您的Swing代码转换为JavaFX:

public class VisibleTest extends Application {

    public class FXVisible extends Tab {

        FXVisible(String id) {
            super(id, new Label(id));

            Timeline thread = new Timeline(
                    new KeyFrame(Duration.ZERO, e -> { 
                        if (isSelected()) {
                            // do expensive stuff
                            System.out.println(id + " is visible");
                        }
                    }),
                    new KeyFrame(Duration.seconds(1))
            );
            thread.setCycleCount(Timeline.INDEFINITE);

            selectedProperty().addListener((selectedProperty, wasSelected, isSelected) -> {
                if (isSelected) {
                    if (thread.getStatus() != Status.RUNNING) {
                        System.out.println(id + " starting thread");
                        thread.play();
                    }
                }
                // else, it is not selected -> content not shown
            });
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        TabPane tabpane = new TabPane();
        tabpane.getTabs().add(new FXVisible("1"));
        tabpane.getTabs().add(new FXVisible("2"));
        tabpane.getTabs().add(new FXVisible("3"));
        // add as many as you want

        primaryStage.setScene(new Scene(tabpane));
        primaryStage.setWidth(600);
        primaryStage.setHeight(500);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
我用JavaFX替换了你的线程。你的问题不是关于这个话题的,所以我不会在这里详细讨论,尽管这是不言自明的

我不明白为什么在Swing示例中,当您可以直接在线程中调用
isVisible()
时,侦听器会更改指示组件是否可见的布尔值(有关线程的说明,请参阅下面的注释)。这就是为什么在上面的代码中,我采用了直接检查
isSelected()
的方法,而不使用自声明的布尔值。如果您需要恢复到您的设计,它相当简单。为了清楚起见,请注意这一点


可以在
selectedProperty()
上用更改侦听器替换
ComponentListener
,并查询新值。只要确保您的示例执行了它应该执行的操作:第一次选择选项卡时,线程/计时器就会启动。在此之后,线程/计时器什么也不做。您可能希望暂停计算以显示非显示内容。再说一次,注意一下,因为这对我来说可能是个错误,否则你就没事了。

谢谢你的回复!让我将我的问题重新表述为更具体的问题:节点如何知道它当前是否正在显示?在Swing中,我可以使用Component.isVisible()或ComponentListener来获取此信息。在JavaFX中,isVisible的行为不同。Swing node.isVisible()与JavaFX node.isVisible()的不同之处是什么?在JavaFX中,这似乎是正确的,即使节点没有显示。“节点如何知道它当前是否正在显示”:它没有。布局和渲染是“自顶向下”执行的。因此,场景的根节点布置其控件,并通过渲染它计算出需要渲染的子节点(或子节点的一部分)来渲染自身。如果每个子节点是父节点,则递归地对其布局执行相同的操作。因此,节点不知道它是否可见(在您的意思中),它只是在必要时被其父节点告知执行自己的布局。谢谢您的解释!因此,节点本身无法检测其是否在屏幕上显示/可见。如果tab2正在显示,您是否知道hbox正在显示?否。如果运行代码
hbox,则不会调用visibleProperty()
hbox.managedProperty()
。如果您为
hbox.isVisible
添加检查,则即使选择了tab1,它也将始终为真。这可能是真的,但如果使用我的原始答案,您知道当选择tab2时,hbox在屏幕上显示/可见。此代码:
tab2.getContent().isVisible()它总是返回true。您说过“这不是真正的问题”。我们非常感谢您为制作一个最小、完整的示例所做的努力,但它似乎没有完全描述您试图解决的问题。看看你是否能想出一个能描述实际问题的例子。请注意,了解“标签何时可见”非常困难,因为您确实需要知道它及其所有祖先是否可见,它是否是显示在正在显示的窗口中的场景的一部分,以及是否有任何其他节点显示在其顶部。感谢您的建议!线程注释:对于Swing代码,
isVisible()
只能从AWT事件调度线程调用:这不是线程安全的方法。AWT事件调度线程上的Swing框架将更改
visible
属性:无法保证其他线程何时或是否“看到”这些更改。OP发布的代码也有缺陷,但可以通过将
doExpensiveStuff
布尔值替换为
AtomicBoolean
,或将其标记为
volatile
@James\D Good point来修复。我用Swing定时器做了一些测试,并且
isVisible()
总是正确更新,不过如果你说的是真的,则需要一些同步。javaFX的
isSelected()
是否也是这种情况?是的,javaFX也是单线程的。(而且,正如我想你知道的,仅仅因为它在测试中正常工作并不等同于知道它总是正常工作。其他平台或JDK版本,甚至只是偶尔的不走运时间,都可能导致类似的情况发生
public class VisibleTest extends Application {

    public class FXVisible extends Tab {

        FXVisible(String id) {
            super(id, new Label(id));

            Timeline thread = new Timeline(
                    new KeyFrame(Duration.ZERO, e -> { 
                        if (isSelected()) {
                            // do expensive stuff
                            System.out.println(id + " is visible");
                        }
                    }),
                    new KeyFrame(Duration.seconds(1))
            );
            thread.setCycleCount(Timeline.INDEFINITE);

            selectedProperty().addListener((selectedProperty, wasSelected, isSelected) -> {
                if (isSelected) {
                    if (thread.getStatus() != Status.RUNNING) {
                        System.out.println(id + " starting thread");
                        thread.play();
                    }
                }
                // else, it is not selected -> content not shown
            });
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        TabPane tabpane = new TabPane();
        tabpane.getTabs().add(new FXVisible("1"));
        tabpane.getTabs().add(new FXVisible("2"));
        tabpane.getTabs().add(new FXVisible("3"));
        // add as many as you want

        primaryStage.setScene(new Scene(tabpane));
        primaryStage.setWidth(600);
        primaryStage.setHeight(500);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}