Java 场景生成器嵌套自定义节点

Java 场景生成器嵌套自定义节点,java,javafx,fxml,scenebuilder,Java,Javafx,Fxml,Scenebuilder,我似乎在Scene Builder 8.4.1中遇到了一个相当严重的错误,其他人以前在不同版本中遇到过这个错误(请参见链接)。错误在于,当我尝试从jar文件导入一个自定义节点,而该节点又包含其他自定义节点时,只会找到嵌套的节点。也就是说,找不到外部节点 所以我想知道。是否有人知道Scene Builder的稳定版本在更新版本的java(8、9或10,如果还没有)中没有这个bug(最好没有其他严重的bug)?我还希望有一个安装向导,它将为我提供一个exe应用程序,而不是一个可运行的jar,以便更好

我似乎在Scene Builder 8.4.1中遇到了一个相当严重的错误,其他人以前在不同版本中遇到过这个错误(请参见链接)。错误在于,当我尝试从jar文件导入一个自定义节点,而该节点又包含其他自定义节点时,只会找到嵌套的节点。也就是说,找不到外部节点

所以我想知道。是否有人知道Scene Builder的稳定版本在更新版本的java(8、9或10,如果还没有)中没有这个bug(最好没有其他严重的bug)?我还希望有一个安装向导,它将为我提供一个exe应用程序,而不是一个可运行的jar,以便更好地与我的IDE集成。如果不存在这种情况,您认为有经验的场景构建者推荐我做什么?我是否应该使所有fxml文档不包含嵌套节点,并在以后手动添加它们

谢谢你的帮助


编辑:可以找到所有源文件。请注意,外部容器是SliderVariable,内部容器是InfoIcon。

您可以在同一个jar中嵌套两个或多个自定义控件,这将在IDE或命令行中正常运行

但是,如果从场景生成器导入该jar,如果某些自定义控件依赖于其他控件,则它们可能无法导入

这是有原因的,最好的是,还有一个简单的解决方案

如何导入自定义控件?

如果查看场景生成器,要导入jar的可能自定义控件,有一个
JarExplorer
类,它有一个
explore

这个方法基本上会遍历jar中的每个类,找出该类是否是应该添加到用户库中的可能自定义组件

最后,它的工作原理是尝试基于该类创建FXML对象:

 entryClass = classLoader.loadClass(className);
 instantiateWithFXMLLoader(entryClass, classLoader);
如果成功,则将该类添加到components集合中

嵌套自定义控件导入失败的原因?

那么,为什么一个嵌套的自定义控件(该控件具有另一个自定义控件作为依赖项)无法导入呢?出现这种情况的原因可以通过调试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
}
因此,这基本上解释了为什么嵌套控件不被导入:内部控件事先在类路径中不可用

可能的解决方案

显然,您可以分别绑定这两个控件,首先导入
InfoIcon
控件,然后只导入
SliderVariable
控件。但是,如果您想在单个依赖项中分发这些控件,这可能会有问题

最佳解决方案

那么,如果内部控件和外部控件在同一个jar中,我们如何在运行时使它们可用呢

这是通过将此外部控制类的类加载器传递给
fxmloader
来完成的。这可以通过调用外部控件中的
fxmloader::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) { }
如果重试,则两个控件都可以作为自定义组件使用


这对我很有用。。。但它需要一些调整。你能发布一些我们可以复制的代码吗?我能在几个小时内完成。但是如果你查看我们一周前的聊天,我已经链接了一个罐子,如果你愿意,你可以尝试一下。我做了,但它不起作用。尽可能发布代码,我来看看。@JoséPereda现在您可以在提供的链接中找到源文件。这很有效!这给我带来了很多麻烦,最后,它成功了!这个评论是对你答案的赞扬。非常感谢。
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) { }