场景生成器嵌套自定义节点
Scene Builder Nested Custom Nodes
我似乎在 Scene Builder 8.4.1 中遇到了一个相当严重的错误,其他人之前也遇到过不同版本的错误(请参阅 this link)。错误是,当我尝试导入一个自定义节点时,该节点又包含来自 jar 文件的其他自定义节点,只能找到嵌套节点。即没有找到外层节点。
所以我想知道。有谁知道 Scene Builder 的稳定版本没有这个错误(最好没有其他严重的错误)的更新版本 java(8、9 或者可能是 10,如果它已经出来了)?我还希望有一个安装向导,它将给我一个 exe 应用程序而不是一个可运行的 jar,以便更好地与我的 IDE 集成。如果这不存在,您有更多 Scene Builder 经验丰富的人建议我做什么?我应该制作没有嵌套节点的所有 fxml 文档,然后再手动添加它们吗?
感谢您的帮助!
编辑: 可以找到所有源文件 here。注意外层容器是SliderVariable,内层是InfoIcon。
您可以在同一个 jar 中嵌套两个或多个自定义控件,这将 运行 从您的 IDE 或命令行中使用。
但是,如果您从 Scene Builder 导入该 jar,如果某些自定义控件依赖于其他控件,则它们可能无法导入。
这是有原因的,最重要的是,还有一个简单的解决方案。
自定义控件是如何导入的?
如果你看看 Scene Builder 的 source code, to import the possible custom controls of a jar, there is a JarExplorer
class, that has an explore
method。
此方法基本上会遍历 jar 中的每个 class,确定 class 是否是应该添加到用户库中的可能的自定义组件。
最后,它的工作原理是尝试基于 class:
创建一个 FXML 对象
entryClass = classLoader.loadClass(className);
instantiateWithFXMLLoader(entryClass, classLoader);
如果成功,class 将添加到组件集合中。
为什么嵌套的自定义控件导入失败?
那么为什么嵌套的自定义控件,也就是依赖另一个自定义控件的控件,导入失败呢?可以找到调试 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
}
在您创建任何类型的自定义控件时记录这非常有用。
在嵌套自定义控件的情况下,如果依赖项已经在 class 路径中可用,并且 class 路径是指 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
所以基本上这解释了为什么没有导入嵌套控件:内部控件事先在 class 路径中不可用。
可能的解决方案
显然,您可以分别捆绑两个控件,首先导入 InfoIcon
控件,然后只导入 SliderVariable
控件。但是,如果您想将这些控件分布在单个依赖项中,这可能会有问题。
最佳解
那么,如果内部控件位于同一个 jar 中,我们如何在 运行 时间内使外部控件可用?
这是通过将此外部控件 class 的 class 加载程序传递给 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) { }
如果您再试一次,您将获得作为自定义组件可用的两个控件。
我似乎在 Scene Builder 8.4.1 中遇到了一个相当严重的错误,其他人之前也遇到过不同版本的错误(请参阅 this link)。错误是,当我尝试导入一个自定义节点时,该节点又包含来自 jar 文件的其他自定义节点,只能找到嵌套节点。即没有找到外层节点。
所以我想知道。有谁知道 Scene Builder 的稳定版本没有这个错误(最好没有其他严重的错误)的更新版本 java(8、9 或者可能是 10,如果它已经出来了)?我还希望有一个安装向导,它将给我一个 exe 应用程序而不是一个可运行的 jar,以便更好地与我的 IDE 集成。如果这不存在,您有更多 Scene Builder 经验丰富的人建议我做什么?我应该制作没有嵌套节点的所有 fxml 文档,然后再手动添加它们吗?
感谢您的帮助!
编辑: 可以找到所有源文件 here。注意外层容器是SliderVariable,内层是InfoIcon。
您可以在同一个 jar 中嵌套两个或多个自定义控件,这将 运行 从您的 IDE 或命令行中使用。
但是,如果您从 Scene Builder 导入该 jar,如果某些自定义控件依赖于其他控件,则它们可能无法导入。
这是有原因的,最重要的是,还有一个简单的解决方案。
自定义控件是如何导入的?
如果你看看 Scene Builder 的 source code, to import the possible custom controls of a jar, there is a JarExplorer
class, that has an explore
method。
此方法基本上会遍历 jar 中的每个 class,确定 class 是否是应该添加到用户库中的可能的自定义组件。
最后,它的工作原理是尝试基于 class:
创建一个 FXML 对象 entryClass = classLoader.loadClass(className);
instantiateWithFXMLLoader(entryClass, classLoader);
如果成功,class 将添加到组件集合中。
为什么嵌套的自定义控件导入失败?
那么为什么嵌套的自定义控件,也就是依赖另一个自定义控件的控件,导入失败呢?可以找到调试 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
}
在您创建任何类型的自定义控件时记录这非常有用。
在嵌套自定义控件的情况下,如果依赖项已经在 class 路径中可用,并且 class 路径是指 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
所以基本上这解释了为什么没有导入嵌套控件:内部控件事先在 class 路径中不可用。
可能的解决方案
显然,您可以分别捆绑两个控件,首先导入 InfoIcon
控件,然后只导入 SliderVariable
控件。但是,如果您想将这些控件分布在单个依赖项中,这可能会有问题。
最佳解
那么,如果内部控件位于同一个 jar 中,我们如何在 运行 时间内使外部控件可用?
这是通过将此外部控件 class 的 class 加载程序传递给 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) { }
如果您再试一次,您将获得作为自定义组件可用的两个控件。