I need to detect if a node is currently displaying. I.e. if my Node is in a TabPane, I need to know if it is in a selected tab or not.
In the example, I want to know when the HBox is displaying.The visibleProperty and managedProperty of Node, does not seem to help me:
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);
}
}
I know, it is possible to listen on the selectedProperty
state of the tab, but this does not solve my real problem.
Node.impl_isTreeVisible()
does what I want, but this is depricated API.
Any ideas?
------------------------------------ update--------------------
I realize the code example above does not explain well what I'm trying to accomplish.
Below is some Swing code that kind of demonstrates what I am trying to accomplish in JavaFX. Detect if the JComponent/Node is visible/shown, and based on that state, start or stop background processes. How would the constructor look like if it was a javaFX class.
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);
}
}
Output when tab1 is selected:
1 is visible true
1 is visible true
1 is visible true
...
Output when tab2 is selected:
2 is visible true
2 is visible true
2 is visible true
...
You can use Tab
's selectedProperty
to know if it is selected or not, and by extension if its content is visible or not. It is a boolean property.
I've converted your Swing code to JavaFX based on your initial JavaFX example:
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);
}
}
I replaced your thread with a JavaFX Timeline
. Your question is not about this topic so I won't go into details here, though it's self explanatory.
I don't understand why in the Swing example you have a listener changing a boolean that indicates if the component is visible or not when you can just call isVisible()
directly in the thread (see comments below for a note about threading). This is why in my code above I took the approach of checking isSelected()
directly with no self-declared boolean. If you need to revert to your design it's rather straightforward. Just noting this for clarity.
The ComponentListener
can be replaced with a change listener on selectedProperty()
and querying the new value. Just be sure that your example does what it's supposed to do: the first time the tab is selected the thread/timer starts. After that the thread/timer does nothing. You might have wanted to pause the computation for non-displaying content. Again, just noting it because it seemed like a potential mistake to me, otherwise you're fine.
Updated answer.
tab2.getContent().isVisible();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With