【问题标题】:Scene Builder Nested Custom Nodes场景生成器嵌套自定义节点
【发布时间】:2018-11-02 02:32:49
【问题描述】:

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

所以我想知道。有谁知道Scene Builder的稳定版本没有这个错误(最好没有其他严重错误),用于更新的java版本(8、9或10,如果它还没有)?我还希望有一个安装向导,它会给我一个 exe 应用程序而不是一个可运行的 jar,以便更好地与我的 IDE 集成。如果这不存在,您有什么更多的 Scene Builder 经验丰富的人建议我做?我是否应该制作没有嵌套节点的所有 fxml 文档,然后手动添加它们?

感谢您的帮助!

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

【问题讨论】:

  • 它适用于我......但它需要一些调整。你能发布一些我们可以重现的代码吗?
  • 我可以在几个小时内完成。但是,如果您查看我们一周前的聊天记录,我已经在其中链接了一个罐子,您可以根据需要尝试一下。
  • 我做了,但没有用。可以的话把代码贴出来,我看看。
  • @JoséPereda 现在您可以在提供的链接中找到源文件。

标签: java javafx fxml scenebuilder


【解决方案1】:

您可以在同一个 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) { }

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

【讨论】:

  • 这行得通!这让我很头疼,最后,它起作用了!
  • 此评论是对您回答的表扬。谢谢!
猜你喜欢
  • 2016-08-09
  • 1970-01-01
  • 2016-03-31
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
  • 1970-01-01
相关资源
最近更新 更多