Scene Builder Вложенные пользовательские узлы

Кажется, я столкнулся с довольно серьезной ошибкой в ​​Scene Builder 8.4.1, с которой другие люди сталкивались ранее для разных версий (см. Эту ссылку). Ошибка заключается в том, что при попытке импортировать пользовательский узел, который, в свою очередь, содержит другие пользовательские узлы из файла JAR, обнаруживаются только вложенные узлы. То есть внешний узел не найден.

Так что мне было интересно. Кто-нибудь знает о стабильном выпуске Scene Builder, в котором нет этой ошибки (и, желательно, других серьезных ошибок) для более поздней версии Java (8, 9 или, возможно, 10, если она еще не выпущена)? Я также хотел бы, чтобы был мастер установки, который даст мне exe-приложение вместо исполняемого jar для лучшей интеграции с моей IDE. Если этого не существует, что вы, более опытные люди Scene Builder, порекомендуете мне сделать? Должен ли я сделать все свои документы fxml без вложенных узлов и добавить их позже вручную?

Спасибо за любую помощь!

РЕДАКТИРОВАТЬ: Все исходные файлы могут быть найдены здесь. Обратите внимание, что внешний контейнер - SliderVariable, а внутренний - InfoIcon.

1 ответ

Решение

Вы можете вкладывать два или более пользовательских элемента управления в один и тот же jar-файл, и он будет отлично работать из вашей IDE или из командной строки.

Но если вы импортируете этот jar-файл из Scene Builder, некоторые пользовательские элементы управления могут не импортироваться, если они зависят от других.

Для этого есть причина, и, что самое приятное, есть и простое решение.

Как пользовательский контроль импортируется?

Если вы посмотрите на исходный код Scene Builder, чтобы импортировать возможные пользовательские элементы управления jar, есть JarExplorer класс, который имеет explore метод.

Этот метод в основном будет проходить через каждый класс в 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 контроль. Но это может быть проблематично, если вы хотите распределить эти элементы управления в одной зависимости.

Лучшее решение

Так как же сделать внутренний контроль внешним во время выполнения, если оба находятся в одном банке?

Это делается путем передачи загрузчика классов этого внешнего класса управления 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) { }

Если вы попытаетесь снова, вы получите оба элемента управления доступными в качестве пользовательских компонентов.

Импорт вложенных элементов управления

Другие вопросы по тегам