I've loaded a Parent from FXML, added it to a Pane within a scene/stage and shown it, then immediately afterwards I lookup components. lookup() returns null for some but non-null for others. Under what circumstances would it do that?
Here's the loading and lookup code:
rootUi = FXMLLoader.load(getClass().getResource("PracticeScreen.fxml"));
// added to Parent within stage and setVisible(true)
analysisGroup = (Pane)rootUi.lookup("#analysisGroup"); // null
stickiesPane = (Pane)rootUi.lookup("#stickiesPane"); // null
scoreScroll = (ScrollPane)rootUi.lookup("#scoreScrollPane"); // GOOD
tintLayer = rootUi.lookup("#tintLayer"); // GOOD
scoreImageView = (ImageView)rootUi.lookup("#scoreImage"); // null
StackPane scoreRenderStack = (StackPane)rootUi.lookup("#scoreRenderStack"); // null
StackPane scoreScrollStack = (StackPane)rootUi.lookup("#scoreScrollStack"); // null
scoreScrollPane comes back OK, but then all of its children come back null.
FXML loaded:
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.effect.DropShadow?>
<?import javafx.scene.effect.Glow?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.Rectangle?>
<?import javafx.scene.text.Font?>
<BorderPane style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<center>
<StackPane id="scoreScreenStack" style="-fx-background-color: blue;" BorderPane.alignment="CENTER">
<children>
<ScrollPane id="scoreScrollPane" fitToHeight="true" hbarPolicy="ALWAYS" vbarPolicy="NEVER">
<content>
<StackPane id="scoreScrollStack">
<children>
<StackPane id="scoreRenderStack" alignment="CENTER_LEFT" StackPane.alignment="CENTER_LEFT">
<children>
<ImageView id="scoreImage" pickOnBounds="true" preserveRatio="true" StackPane.alignment="CENTER_LEFT" />
<Pane id="analysisGroup" />
<Pane id="stickiesPane" minHeight="200.0" minWidth="200.0" />
<Rectangle id="playbackCursor" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="150.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="0.0" visible="false" width="2.0">
<effect>
<DropShadow color="DODGERBLUE" />
</effect>
</Rectangle>
</children>
</StackPane>
</children>
</StackPane>
</content>
</ScrollPane>
<BorderPane pickOnBounds="false">
<top>
<StackPane BorderPane.alignment="CENTER">
<children>
<BorderPane>
<left>
<Label id="recordingIndicator" text="Listening..." textFill="#00a7ff" BorderPane.alignment="CENTER">
<font>
<Font size="18.0" />
</font>
<effect>
<Glow level="0.86" />
</effect>
<BorderPane.margin>
<Insets />
</BorderPane.margin>
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</padding>
</Label>
</left>
<right>
<HBox BorderPane.alignment="CENTER">
<children>
<ToggleButton id="finalPerformance" mnemonicParsing="false" text="Final Performance" />
</children>
<BorderPane.margin>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
</BorderPane.margin>
</HBox>
</right>
</BorderPane>
</children>
</StackPane>
</top>
</BorderPane>
<Pane id="tintLayer" opacity="0.0" pickOnBounds="false" style="-fx-background-color: #00a7ff;" visible="false" />
</children>
</StackPane>
</center>
</BorderPane>
Lookups are not guaranteed to work until CSS has been applied to the scene. This typically happens on the first layout pass (i.e. the first time the scene graph is rendered to the screen), and in some cases may even happen later than that. Lookups are generally not recommended as a mechanism to get nodes you have defined in the scene graph.
The recommended way to reference controls defined in FXML is to use a controller class. Change all your id
attributes in the FXML to fx:id
attributes, and add a controller class attribute to the root element:
<BorderPane style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.my.company.PracticeScreenController">
<center>
<StackPane fx:id="scoreScreenStack" style="-fx-background-color: blue;" BorderPane.alignment="CENTER">
<children>
<ScrollPane fx:id="scoreScrollPane" fitToHeight="true" hbarPolicy="ALWAYS" vbarPolicy="NEVER">
<content>
<StackPane fx:id="scoreScrollStack">
<!-- etc -->
</StackPane>
</content>
</ScrollPane>
</children>
</StackPane>
</center>
</BorderPane>
Now define a controller class with corresponding @FXML
-annotated fields. You can access those fields in the initialize()
method (note they will not have been initialized when the constructor is called):
package com.my.company ;
// imports...
public class PracticeScreenController {
@FXML
private StackPane scoreScreenStack ;
@FXML
private ScrollPane scoreScrollPane ;
@FXML
private ScrollPane scoreScrollStack ;
// etc...
public void initialize() {
// configure controls here, as needed...
}
}
If there is some pressing need to retrieve references to nodes defined in the FXML file from the point where the FXML file is loaded, instead of in the controller, you can do so via the FXMLLoader
's namespace. Again, I should emphasize that this is not the recommended approach, and you should really use a controller class to access these, but this does provide another solution.
Using the same fx:id
attributes shown in the FXML above, you can do:
FXMLLoader loader = new FXMLLoader(getClass().getResource("PracticeScreen.fxml"));
rootUi = loader.load();
Map<String, Object> namespace = loader.getNamespace();
// added to Parent within stage and setVisible(true)
analysisGroup = (Pane)namespace.get("analysisGroup");
stickiesPane = (Pane)namespace.get("stickiesPane");
scoreScroll = (ScrollPane)namespace.get("scoreScrollPane");
tintLayer = (Pane)namespace.get("tintLayer");
scoreImageView = (ImageView)namespace.get("scoreImage");
StackPane scoreRenderStack = (StackPane)namespace.get("scoreRenderStack");
StackPane scoreScrollStack = (StackPane)namespace.get("scoreScrollStack");
It is because lookup() only looks at children, not "content" as in the case of scoreScrollPane which is a ScrollPane. ScrollPane's content is not included in its children. Also, rootUi.getScene().lookup("#scoreScrollPane")
returns null too. You must instead do:
scoreImageView = (ImageView)scoreScroll.getContent().lookup("#scoreImage"); // GOOD now
来源:https://stackoverflow.com/questions/36769899/javafx-node-lookup-returning-null-only-for-some-elements-in-parent-loaded-with