我似乎在Scene Builder 8.4.1中遇到了一个非常严重的错误,其他人之前遇到过不同版本的错误(请参阅this链接)。问题在于,当我尝试导入自定义节点时,该节点又包含jar文件中的其他自定义节点,只能找到嵌套节点。也就是说,找不到外部节点。
所以我在想。有没有人知道一个稳定版本的Scene Builder没有这个bug(最好没有其他严重的版本)用于更新版本的java(如果它还没出现,那么8,9或10)?我还希望有一个安装向导,它将为我提供一个exe应用程序,而不是一个可运行的jar,以便更好地与我的IDE集成。如果这不存在,那么您对Scene Builder的更多经验是什么让我推荐的呢?我应该在没有嵌套节点的情况下制作所有fxml文档,并在以后手动添加它们吗?
感谢您的帮助!
编辑:可以找到所有源文件here。请注意,外部容器是SliderVariable,内部是InfoIcon。
答案 0 :(得分:4)
您可以在同一个jar中嵌套两个或多个自定义控件,并且可以从IDE或命令行运行。
但是如果从Scene Builder导入该jar,如果某些自定义控件依赖于其他自定义控件,则可能无法导入它们。
这是有原因的,也是最好的部分,也有一个简单的解决方案。
如何导入自定义控件?
如果您查看Scene Builder的source code,要导入jar的可能自定义控件,就会有一个JarExplorer
类,其中包含explore
method
此方法基本上将遍历jar中的每个类,查明该类是否是可能添加到用户库中的自定义组件。
最后,它的工作原理是尝试基于该类创建FXML对象:
entryClass = classLoader.loadClass(className);
instantiateWithFXMLLoader(entryClass, classLoader);
如果成功,则将该类添加到组件集合中。
为什么嵌套的自定义控件无法导入?
那么为什么嵌套的自定义控件(另一个自定义控件作为依赖项的控件)无法导入?可以找到调试Scene Builder的原因,并打印出你得到的异常:
try {
instantiateWithFXMLLoader(entryClass, classLoader);
} catch (RuntimeException | IOException x) {
status = JarReportEntry.Status.CANNOT_INSTANTIATE;
x.printStackTrace(); // <-- print exception
} catch (Error | ClassNotFoundException x) {
status = JarReportEntry.Status.CANNOT_LOAD;
x.printStackTrace(); // <-- print exception
}
在创建任何类型的自定义控件时,记录此功能非常有用。
在嵌套自定义控件的情况下,如果依赖关系已经在类路径中可用,并且类路径我指的是事先由Scene Builder加载的所有依赖关系,则不会出现问题。但如果它不可用,FXMLLoader将无法创建此控件的有效实例。
让我们试试你的SliderVariable
控件。如果你打印出异常,你会看到如下内容:
javafx.fxml.LoadException:
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2425)
at com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer.instantiateWithFXMLLoader(JarExplorer.java:110)
... 9 more
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException:
file:.../SliderVariable-1.0-SNAPSHOT-shaded!/com/coolcompany/slidervariable/SliderVariable.fxml
...
Caused by: java.lang.ClassNotFoundException: com.coolcompany.infoicon.InfoIcon
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at javafx.fxml.FXMLLoader.loadTypeForPackage(FXMLLoader.java:2916)
at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2905)
at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2846)
... 26 more
所以基本上这解释了为什么不导入嵌套控件:内部控件事先在类路径中不可用。
可能的解决方案
显然,您可以单独捆绑两个控件,首先导入InfoIcon
控件,然后只导入SliderVariable
控件。但是,如果您希望将这些控件分布在单个依赖项中,则可能会出现问题。
最佳解决方案
那么如果两者都在同一个jar中,我们如何在运行时将内部控件提供给外部控件呢?
这是通过将此外部控件类的类加载器传递给FXMLLoader
来完成的。这可以通过调用外部控件中的FXMLLoader::setClassLoader
方法来完成。
在你的情况下:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("SliderVariable.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
// set FXMLLoader's classloader!
fxmlLoader.setClassLoader(getClass().getClassLoader());
try {
fxmlLoader.load();
} catch (IOException exception) { }
如果再试一次,您将获得两个控件作为自定义组件。