环境:使用Scene Builder的Java FX 8
上下文:我尝试使用TableView
创建一个包含TableColumn
和ObservableList
的简单阶段。调用create_ConfigStage()
来创建阶段,并初始化initialize()
。 FXML的控制器类是ControllerA
(这个类),一切都很好,直到...
问题:无法弄清楚为什么在words.size
中调用create_ConfigStage()
会导致NullPointerException(或0),但在initialize()
中调用相同内容会产生问题。
问题类型:java.lang.NullPointerException
或在initialized()
中初始化的精确值未在创建阶段后初始化(或类似内容)。
仅供参考:没有其他内容混淆words
变量甚至templates
变量。
附加测试:
只需添加按钮即可使用button
和handleButton
来测试单词的大小。结果还可以。但仍然在同一个旧地方无效。
只需添加字符串testVal
即可查看值是否有所变化。显然,它没有。
class ControllerA {
@FXML TableView<Words> templatesTable;
@FXML TableColumn<Words, String> templateHeaders;
@FXML Button button;
Stage stage;
ObservableList<Words> templates;
List<Words> words = new ArrayList<Words>();
String testVal = "Nothing Happened";
@FXML
public void initialize(){
words = FileUtil.readMacroFile(new File("macros.dat"));
templates = FXCollections.observableArrayList();
templates.addAll(words);
templatesTable.setItems(templates);
templateHeaders.setCellValueFactory(new PropertyValueFactory<Words, String>("header"));
testVal = "Something Happened";
System.out.println(words.size()); //<<-- size = 5 (THIS IS PRINTED)
}
public void create_ConfigStage() {
stage = new Stage();
FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemResource("windows/configureTemplates.fxml"));
AnchorPane root = null;
try {
root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(testVal); //This one prints "Nothing Happened" instead of "Something happened"!!!
System.out.println(words.size()); //<<-- The line showing null pointer(if wasnt initialized in initializer() else shows 0)
}
@FXML
public void handleButton(){
System.out.println(words.size()); // <<-- this one prints 5(THIS IS PRINTED AS WELL)
}
调用create_ConfigStage()
的代码public class Main_Controller {
@FXML Button createConfig;
@FXML
public void handleConfigTemplateRequest(){
main.getControllerA().create_ConfigStage();
//This method is invoked by "createConfig" button
}
}
这是调用的类创建主要阶段
public class Main extends Application{
public void start(Stage mainStage) throws Exception{
this.primaryStage = mainStage;
mainStage.setTitle("Scratch GLSL Editor");
FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemResource("main.fxml")); //main.fxml's controller is class Main_Controller
AnchorPane root = loader.load();
Scene mainScene = new Scene(root);
mainStage.setScene(mainScene);
mainStage.show();
ControllerA controllerA = new ControllerA();
controllerA.setMain(this); //to get the controller from this class
setControllerA(controllerA);
}
//setter and getter for ControllerA here
public static void main(String[] args){
launch(args);
}
}
答案 0 :(得分:2)
您调用ControllerA
的{{1}}实例由行
create_ConfigStage()
换句话说,它不是作为ControllerA controllerA = new ControllerA();
load(...)
进程的一部分为您创建的。因此,任何FXMLLoader
注入的字段都不会被初始化,并且永远不会在该实例上调用FXML
方法。
稍后,当您加载FXML时,initialize()
会创建另一个 FXMLLoader
实例,并且ControllerA
进程的一部分会调用load(...)
} 那个实例。按下该按钮时,将在同一initialize()
创建的实例上调用事件处理程序方法。
因此,FXMLLoader
已在words
创建的实例中的FileUtil.readMacroFile(...)
方法中正确初始化,但在您自己创建的实例中从未正确初始化。因此,对于您调用FXMLLoader
的实例,create_ConfigStage()
是您在声明中内联创建的空words
(如果省略ArrayList
,则为null
宣言)。
由于您还没有真正展示FXML文件与各种控制器之间的关系,但是要获取由其加载的控制器实例,因此您并未充分清楚自己要做什么。 = new ArrayList<Words>()
你会使用像
FXMLLoader
使用FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemResource("windows/configureTemplates.fxml"));
AnchorPane root = null;
try {
root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.showAndWait();
ControllerA controllerA = loader.getController();
System.out.println(controllerA.getWords().size());
} catch (IOException e) {
e.printStackTrace();
}
中定义的合适getWords()
方法:
ControllerA
显然你无法在public List<Words> getWords() {
return words ;
}
的实例方法中执行此操作,因为你创建了鸡与蛋的情况(你需要从ControllerA
获取控制器实例,但是在你拥有控制器实例之前,你不会调用FXMLLoader
。
您遇到问题的原因是您试图反转通常的FXML加载过程。通常的过程是这样的:
FXMLLoader
FXMLLoader
load(...)
FXMLLoader
读取FXML文件FXMLLoader
创建控制器,由FXML文件中的FXMLLoader
属性指定换句话说,FXML文件(视图)会为您实例化控制器。
您正在尝试以相反的方式执行操作,即您尝试让控制器实例创建fx:controller
并随后加载视图。你可以用这种方式工作,但你必须以不同的方式进行设置。以下假设您没有在其他地方加载此FXML文件(您至少必须相应地修改该代码)。
首先,从FXMLLoader
中移除fx:controller
属性(因为您要在创建configureTemplates.fxml
之前创建控制器,否则您不会...我希望加载器再次实例化控制器类。
然后按如下方式修改FXMLLoader
方法:
create_ConfigStage()
您可能还想阅读this related technique。
答案 1 :(得分:0)
我认为这一行使得单词为null:
words = FileUtil.readMacroFile()
因此,您需要在FileUtil中检查该方法,以查看它何时返回null。
答案 2 :(得分:0)
在尝试构建通过FXML文件定义内容的高级对话框时,我遇到了类似的问题。
对我有用的是:
请注意不要在initialize()方法中使用该对话框,因为该对话框尚未准备好,我花了一些时间弄清楚为什么,James_D的回答对我有很大帮助!
对话框的FXML定义:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="popup" fx:id="mainPane"
xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.controllers.MyDialogController"/>
对话框的控制器类:
public class MyDialogController implements Initializable {
private static ObservationTactileDialogController instance;
// FxmlDialog.java is a custom class extending Dialog<>
// and provides a loader
private static FxmlDialog<LinkedList<ObservationDTO>> dialog =
new FxmlDialog<>("/fxml/dialog.fxml");
private ResultType result;
@Override
public void initialize(URL location, ResourceBundle resources) {
instance = this;
// do your own stuff here...
}
public ResultType edit() {
dialog.getDialogStage().showAndWait();
return result;
}
public MyDialogController getInstance() {
return instance;
}
// some business logic to built the result
}
然后,在任何其他控制器方法中,您都可以像下面这样使用此对话框:
MyDialogController dialogController = MyDialogController.getInstance();
ResultType result = dialogController.edit();
我希望这会有所帮助!