Scene Builder嵌套的自定义节点

Iva*_*son 2 java javafx fxml scenebuilder

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

所以我想知道。有谁知道一个稳定的Scene Builder版本,该版本对于Java的较新版本(8、9或10,如果还没有发布的话)没有此错误(最好没有其他严重错误)?我还希望有一个安装向导,它将为我提供一个exe应用程序而不是一个可运行的jar,以便与我的IDE更好地集成。如果不存在,那么您还有更多需要Scene Builder经验的人推荐我做什么?我是否应该使所有fxml文档都没有嵌套节点,并在以后手动添加它们?

谢谢你的帮助!

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

Jos*_*eda 5

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

但是,如果从Scene Builder导入该jar,则某些自定义控件如果依赖于其他控件,则可能无法导入。

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

如何导入自定义控件?

如果您查看Scene Builder的源代码,以导入jar的可能的自定义控件,则有一个JarExplorer类,其中包含一个explore method

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

最后,如何工作是通过尝试基于该类创建FXML对象:

 entryClass = classLoader.loadClass(className);
 instantiateWithFXMLLoader(entryClass, classLoader);
Run Code Online (Sandbox Code Playgroud)

如果成功,则将该类添加到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
}
Run Code Online (Sandbox Code Playgroud)

创建任何类型的自定义控件时,记录此内容确实很有帮助。

对于嵌套的自定义控件,如果依赖关系在类路径中已经可用,并且按类路径,我是指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
Run Code Online (Sandbox Code Playgroud)

因此,从根本上讲,这解释了为什么不导入嵌套控件的原因:内部控件事先在类路径中不可用。

可能的解决方案

显然,您可以将两个控件分别捆绑在一起,先导入该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) { }
Run Code Online (Sandbox Code Playgroud)

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

导入嵌套控件