是否有一种方法来处理典型的锅炉板代码来实例化和加载FXML?
典型的版本:
public void start(Stage primaryStage)
{
URL fxmlUrl = this.getClass().getResource( /* your string path*/ );
FXMLLoader loader = new FXMLLoader(fxmlUrl);
try
{
primaryStage = loader.load();
this.controller = loader.getController();
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
primaryStage.show();
}注对于上面的代码,支持FXML文件以<Stage></Stage>作为根。这只是我的偏好。加载和匹配到FXML根可以进行相应的调整。无论如何,加载FXML文件并获得对控制器的引用是关键部分(在大多数情况下)。
我尝试创建一个实用程序方法,如下所示,以帮助防止这个锅炉板代码:
<R, C> void loadFXML(R root, C controller, String path)
{
URL fxmlUrl = this.getClass().getResource( path );
FXMLLoader loader = new FXMLLoader(fxmlUrl);
try
{
root = loader.load();
controller = loader.getController();
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}但是,在某些阶段存在空指针错误,或者加载不正确。我相信这与泛型和/或铸造有关。
这段代码怎么能被修改呢?或者,也许可以建议另一种类型的代码来解决这个问题?
发布于 2014-09-06 02:55:12
问题是(可能;您没有显示您的Null指针异常来自何处),您只是设置局部变量root和controller,然后让它们超出作用域。
请记住,Java以逐值的方式处理方法参数:因此该方法创建root和controller引用的本地副本。当您的方法调用
root = loader.load();
controller = loader.getController();它只是更新本地引用,而不是传递给方法的引用。所以如果你这么做
Parent root = null ;
MyController controller = null ;
loadFXML(root, controller, "Main.fxml");
System.out.println(root);
System.out.println(controller);您将看到值null。
一个解决办法是传递对象的可变包装器,并更新这些包装器。ObjectProperty对此很有帮助:
<R, C> void loadFXML(String path, ObjectProperty<R> root, ObjectProperty<C> controller)
{
URL fxmlUrl = this.getClass().getResource( path );
FXMLLoader loader = new FXMLLoader(fxmlUrl);
try
{
root.set(loader.load());
controller.set(loader.getController());
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}另一个选项是创建一个封装这两个值并返回其实例的类:
public class ViewController<V,C> {
private final V view ;
private final C controller ;
public ViewController(V view, C controller) {
this.view = view ;
this.controller = controller ;
}
public V getView() {
return view ;
}
public C getController() {
return controller ;
}
}然后
<R, C> ViewController<R, C> loadFXML(String path)
{
URL fxmlUrl = this.getClass().getResource( path );
FXMLLoader loader = new FXMLLoader(fxmlUrl);
try
{
R root = loader.load();
C controller = loader.getController();
return new ViewController<R,C>(root, controller);
}
catch(IOException ioe)
{
ioe.printStackTrace();
throw new UncheckedIOException(ioe); // this is likely fatal, so just throw an unchecked exception
}
}不过,你是否在细节上得到了任何节省,这是值得商榷的:
ViewController<Parent, MyController> viewController = loadFXML("Main.fxml");
Parent root = viewController.getView();
MyController controller = viewController.getController();最后一个注意是,在几乎所有用例中,您实际上只在事后对控制器做任何重要的工作;您通常只是将视图设置为场景的根,或者将其添加到另一个布局窗格中。您可以通过让方法接受Consumer<R>并返回控制器来利用这一点(在Java 8中):
public <R, C> C loadFXML(Consumer<R> rootHandler, String path) {
URL fxmlUrl = this.getClass().getResource( path );
FXMLLoader loader = new FXMLLoader(fxmlUrl);
try
{
R root = loader.load();
C controller = loader.getController();
rootHandler.accept(root);
return controller ;
}
catch(IOException ioe)
{
ioe.printStackTrace();
throw new UncheckedIOException(ioe); // this is likely fatal, so just throw an unchecked exception
}
},您可以使用类似的方法调用
Scene scene = new Scene();
MyController controller = loadFXML(scene::setRoot, "Main.fxml");https://stackoverflow.com/questions/25696223
复制相似问题