Вы можете вкладывать два или более пользовательских элемента управления в один и тот же файл jar, и он будет отлично работать из вашей IDE или из командной строки.
Но если вы импортируете этот файл из Scene Builder, некоторые из пользовательскихэлемент управления может быть не импортирован, если они зависят от других.
Для этого есть причина, и, что самое приятное, есть простое решение.
Как импортируются пользовательские элементы управления?
Если вы посмотрите на исходный код Scene Builder , чтобы импортировать возможные пользовательские элементы управления банка, существует класс JarExplorer
,у которого есть explore
метод .
Этот метод в основном будет проходить через каждый класс в банке, выясняя, является ли этот класс возможным пользовательским компонентом, который должен быть добавлен в библиотеку пользователя..
В конце концов, как это работает, пытаясь создать объект FXML на основе этого класса:
entryClass = classLoader.loadClass(className);
instantiateWithFXMLLoader(entryClass, classLoader);
Если это удается, класс добавляется в коллекцию компонентов.
Why не удается импортировать вложенный настраиваемый элемент управления?
Так почему же не удается импортировать вложенный настраиваемый элемент управления, представляющий собой элемент управления с другим настраиваемым элементом управления в качестве зависимости?Причину этого можно найти в отладке 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) { }
Если вы попытаетесь снова, вы получите оба элемента управления доступными в качестве пользовательских компонентов.