我正在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;
}
答案 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();