JavaFXGridPane搜索返回空节点

JavaFXGridPane搜索返回空节点,java,javafx,nullpointerexception,Java,Javafx,Nullpointerexception,我想从FXML文件在JavaFX中实现我自己的自定义对话框,并希望以编程方式更改对话框的标题和消息。我的.fxml文件具有以下结构:对于一个典型的对话框,我在本例中使用了一个错误对话框: <StackPane prefHeight="140.0" prefWidth="290.0" style="-fx-background-color: #333333;" stylesheets="/style.css" xmlns="http://javafx.com/javaf

我想从FXML文件在JavaFX中实现我自己的自定义对话框,并希望以编程方式更改对话框的标题和消息。我的.fxml文件具有以下结构:对于一个典型的对话框,我在本例中使用了一个错误对话框:

<StackPane prefHeight="140.0" prefWidth="290.0" style="-fx-background-color: #333333;" stylesheets="/style.css"
           xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
           fx:controller="apps.utils.Dialogs">
    <children>
        <BorderPane>
            <center>
                <GridPane>
                    <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
                    </columnConstraints>
                    <rowConstraints>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
                    </rowConstraints>
                    <children>
                        <Label text="Error msg" textFill="LIGHTGRAY"/>
                        <Text fill="LIGHTGRAY" strokeType="OUTSIDE" strokeWidth="0.0"
                              text="My custom error message here"
                              wrappingWidth="250.0" GridPane.rowIndex="1">
                            <GridPane.margin>
                                <Insets top="10.0"/>
                            </GridPane.margin>
                        </Text>
                        <JFXButton onMouseClicked="#close"
                                   style="-fx-background-color: #da294f; -fx-border-radius: 5; -fx-background-radius: 5;"
                                   text="Retry" textFill="LIGHTGRAY" GridPane.halignment="CENTER" GridPane.rowIndex="2"
                                   GridPane.valignment="BASELINE">
                            <GridPane.margin>
                                <Insets top="20.0"/>
                            </GridPane.margin>
                        </JFXButton>
                    </children>
                </GridPane>
            </center>
            <StackPane.margin>
                <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
            </StackPane.margin>
        </BorderPane>
    </children>
</StackPane>
用于在控制器类中显示对话框的方法:

    public static void showDialog(StackPane rootPane, BorderPane rootBorderPane, URL dialogFile, String title, String message) throws IOException {
  rootBorderPane.setEffect(boxBlur);
        FXMLLoader loader = new FXMLLoader(dialogFile);
        loader.setClassLoader(cachingClassLoader);
        StackPane dialog = loader.load();
        ObservableList<Node> components = dialog.getChildren();
        BorderPane pane = (BorderPane) components.get(0);
        GridPane content = (GridPane) pane.getCenter();
        System.out.println("row: " + content.getRowCount() + " col:" + content.getColumnCount()); //prints Row 3, col 1

        Label dialogTitle = (Label) findNodeFromIndex(content, 0, 0);
        dialogTitle.setText(title);
        Text dialogMessage = (Text) findNodeFromIndex(content, 0, 1);
        dialogMessage.setText(message);
        //rest of the code to show dialog...
}

然后我有一个功能,可以像这样在Gridpane中搜索:

   private static Node findNodeFromIndex(GridPane gridPane, int col, int row) {
        for (int i = 0; i < gridPane.getChildren().size(); i++) {
            Node node = gridPane.getChildren().get(i);
            if (GridPane.getRowIndex(node) == row && GridPane.getColumnIndex(node) == col) {
                return node;
            }
        }
        return null;
    }
然后,我只需使用对话框类型、标题以及自定义标题/正文文本作为参数调用showDialog

现在我的问题是,我在setText方法上得到了一个NullPointerException。我不明白这是为什么。我怎样才能解决这个问题

我认为findNodeFromIndex返回null,因为您没有在列0中的节点上显式设置列索引,等等

像这样手动搜索场景图根本不是正确的方法。对布局的任何微小更改都将完全破坏此代码。相反,只需在控制器类中定义方法来设置标题和消息:

public class Dialogs {

    @FXML
    private Label titleLabel ;

    @FXML
    private Text messageText ;

    public void setTitle(String title) {
        titleLabel.setText(title);
    }

    public void setMessage(String message) {
        messageText.setText(message);
    }

    // ...
}
并在FXML中的元素上设置fx:id:

然后您可以调用这些方法:

public static void showDialog(StackPane rootPane, BorderPane rootBorderPane, URL dialogFile, String title, String message) throws IOException {
    rootBorderPane.setEffect(boxBlur);

    FXMLLoader loader = new FXMLLoader(dialogFile);
    loader.setClassLoader(cachingClassLoader);
    StackPane dialog = loader.load();
    Dialogs controller = loader.getController();
    controller.setTitle(title);
    controller.setMessage(message);
    //rest of the code to show dialog...

}

由于未对某些元素设置行索引,因此在0处搜索时,这些元素将不匹配,因此findNodeFromIndex返回null。无论如何,这是一种非常丑陋的方式;为什么不获取对控制器的引用,并在控制器中定义方法来设置标题和消息?然后避免像这样在场景图中搜索,请注意,如果对布局进行任何轻微修改,代码将被破坏得有多严重。哦,可能就是这样。我没有检查fxml文件。我只签入了Scenebuilder,它们显示了行索引。获取控制器的引用是我尝试的第一件事。使用@FXML,但它始终返回空值。我想我必须自己硬编码,但这是我想要避免的。我想得很好,因为我确信我总是把标题和消息作为第一行和第二行,然后我就可以这样做了。我也不想在这上面花太多时间,但事后看来,我花在这另一种方法上的时间现在可能没有什么不同。???只需在调用loader后调用loader.getController,我相信我就是这么做的。我觉得我的控件标题有点不对劲。我知道必须在fxml文件名之后连接控制器。但是你会如何处理这个_角色呢?例如,如果文件的id是my_dialog.fxml。控制器应该是my_dialogController还是myDialogController?我完全不知道你在最后的评论中说的是什么。您发布的代码中没有可见的标记。谢谢。好东西。我错过了loader.getController行