打开文件并将其内容设置为JavaFX NullPointer中的TextArea

时间:2014-09-24 16:52:15

标签: java javafx fxml

我正在JavaFX中构建一个小编辑器应用程序,我正在使用FXML文件和控制器。我有几个FXML文件(每个文件都有相应的控制器)。我要做的就是从'root'fxml(包含MenuBar)处理'open file'操作,然后将该文件传递给另一个控制器,该控制器应该“解析”它然后显示它在指定的TextArea。但我得到一个NullPointerException,我无法理解。调试也没有帮助我。

以下是我的一些代码:

首先,来自我的RootLayout.fxml的方法,其中包含用于打开文件的菜单项:

@FXML
private void handleOpen() {
    fileChooser = new FileChooser();
    fileChooser.setInitialDirectory(new File("."));
    extFilter1 = new FileChooser.ExtensionFilter("XML Documents (*.xml)", "*.xml");
    extFilter2 = new FileChooser.ExtensionFilter("All Files", "*.*");
    fileChooser.getExtensionFilters().addAll(extFilter1, extFilter2);

    xmlFile = fileChooser.showOpenDialog(mainApp.getPrimaryStage());

    if(xmlFile != null) {
       EditorOverviewController controller = new EditorOverviewController();
        try {
            controller.loadXmlFile(xmlFile);
        } catch (IOException ex) {
            Logger.getLogger(RootLayoutController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

现在是另一个控制器中的loadXmlFile方法:

public void loadXmlFile(File file) throws IOException {
    StringBuilder sb = new StringBuilder();
    try (FileInputStream fis = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(fis)) {
        while (bis.available() > 0) {
            sb.append((char) bis.read());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        sourceEditor.setText(sb.toString());
    } catch (Exception ex) {
        Logger.getLogger(RootLayoutController.class.getName()).log(Level.SEVERE, null, ex);
    }
}

其中sourceEditor是我的fx:id文本区域,在控制器顶部正常注释:

@FXML
private TextArea sourceEditor;

我在NullPointer得到了这个:

java.lang.NullPointerException
    at mainApp.view.EditorOverviewController.loadXmlFile(EditorOverviewController.java:103)
    at uimlbuddy.view.RootLayoutController.handleOpen(RootLayoutController.java:81)

关于我究竟做错了什么的任何线索?提前谢谢!

更新

我正在加载我的fxml文件并在我的MainApp类中获取这样的控制器:

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    primaryStage.getIcons().add(new Image("/assets/app_icon.png"));

    initRootLayout();

    showEditorOverview();
}

 /**
 * Initializes the root layout.
 */
private void initRootLayout() {
    try {
        // Load root layout from fxml file.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
        rootLayout = (BorderPane) loader.load();

        // Show the scene containing the root layout.
        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);

        // Give the controller access to the main app.
        RootLayoutController controller = loader.getController();
        controller.setMainApp(this);

        primaryStage.show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * Shows the editor overview with all panels inside the root layout.
 */
private void showEditorOverview() {
    try {
        // Load person overview.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/EditorOverview.fxml"));
        AnchorPane editorOverview = (AnchorPane) loader.load();

        // Set editor overview into the center of root layout.
        rootLayout.setCenter(editorOverview);

        EditorOverviewController controller = loader.getController();
        controller.setMainApp(this);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

然后在每个控制器中我都有这个方法setMainApp来给自己一个引用:

/**
 * Is called by the main application to give a reference back to itself.
 *
 * @param mainApp
 */
public void setMainApp(MainApp mainApp) {
    this.mainApp= mainApp;
}

2 个答案:

答案 0 :(得分:2)

RootLayoutController中公开一个属性(我假设这是带有handleOpen()方法的控制器)来保存所选文件:

public class RootLayoutController {

    private final ObjectProperty<File> selectedFile = new SimpleObjectProperty<>(this, "selectedFile");
    public final ObjectProperty<File> selectedFileProperty() {
        return selectedFile ;
    }
    public final File getSelectedFile() {
        return selectedFile.get();
    }
    public final void setSelectedFile(File file) {
        this.selectedFile.set(file);
    }

    @FXML
    private void handleOpen() {
        fileChooser = new FileChooser();
        fileChooser.setInitialDirectory(new File("."));
        extFilter1 = new FileChooser.ExtensionFilter("XML Documents (*.xml)", "*.xml");
        extFilter2 = new FileChooser.ExtensionFilter("All Files", "*.*");
        fileChooser.getExtensionFilters().addAll(extFilter1, extFilter2);

        xmlFile = fileChooser.showOpenDialog(mainApp.getPrimaryStage());

        if (xmlFile != null) {
            setSelectedFile(xmlFile);
        }
    }

    // ...
}

然后在您的主应用程序中,您可以观察该属性并在其更改时更新另一个控制器:

private void initRootLayout() {
    try {
        // Load root layout from fxml file.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
        rootLayout = (BorderPane) loader.load();

        // Show the scene containing the root layout.
        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);

        // Give the controller access to the main app.
        RootLayoutController controller = loader.getController();

        // really should not need this ugly coupling from your controllers back to the app
        // controller.setMainApp(this);

        controller.selectedFileProperty().addListener((obs, oldFile, newFile) -> {
            if (newFile != null) {
                editorOverviewController.loadXmlFile(newFile);
            }
        }

        primaryStage.show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

您显然需要editorOverviewController成为主应用中的实例字段,可以使用showEditorOverview方法进行初始化:

public class MainApp extends Application {
    private EditorOverviewController editorOverviewController ;

    @Override
    public void start(Stage primaryStage) throws Exception {
        // as before ...
    }

    private void initRootLayout() {
        // as above ...
    }

    private void showEditorOverview() {
        try {
            // Load person overview.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource("view/EditorOverview.fxml"));
            AnchorPane editorOverview = (AnchorPane) loader.load();

            // Set editor overview into the center of root layout.
            rootLayout.setCenter(editorOverview);

            // note subtle change here:
            this.editorOverviewController = loader.getController();

            // again, should not need this:
            // controller.setMainApp(this);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

答案 1 :(得分:1)

您必须加载第二个FXML,然后从中提取控制器。使用:

FXMLoader loader = new FXMLoader(path to your fxml);
loader.load();
EditorOverviewController controller = (EditorOverviewController)loader.getController();