JavaFX Node.lookup() returning null only for some elements in Parent loaded with FXMLLoader

巧了我就是萌 提交于 2019-12-02 03:18:47

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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!