Scene Builder嵌套自定义节点

时间:2018-05-23 07:46:01

标签: java javafx fxml scenebuilder

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

所以我在想。有没有人知道一个稳定版本的Scene Builder没有这个bug(最好没有其他严重的版本)用于更新版本的java(如果它还没出现,那么8,9或10)?我还希望有一个安装向导,它将为我提供一个exe应用程序,而不是一个可运行的jar,以便更好地与我的IDE集成。如果这不存在,那么您对Scene Builder的更多经验是什么让我推荐的呢?我应该在没有嵌套节点的情况下制作所有fxml文档,并在以后手动添加它们吗?

感谢您的帮助!

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

1 个答案:

答案 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) { }

如果再试一次,您将获得两个控件作为自定义组件。

Importing nested controls