Я надеялся, что кто-нибудь сможет объяснить мне, почему SceneBuilder настолько темпераментен, когда дело доходит до импорта пользовательских элементов управления.
Возьмем, к примеру, относительно простой пользовательский элемент управления (только в качестве примера):
public class HybridControl extends VBox{
final private Controller ctrlr;
public CustomComboBox(){
this.ctrlr = this.Load();
}
private Controller Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("Hybrid.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch (IOException ex){
throw new IllegalStateException(ex);
}
final Controller ctrlr = loader.getController();
assert ctrlr != null;
return ctrlr;
}
/*Custom Stuff Here*/
}
И тогда у вас есть класс Controller здесь:
public class Controller implements Initializable{
/*FXML Variables Here*/
@Override public void initialize(URL location, ResourceBundle resources){
/*Initialization Stuff Here*/
}
}
Это прекрасно работает. .jar прекрасно компилируется, SceneBuilder прекрасно читает .jar, он отлично импортирует элемент управления, и это здорово.
Меня раздражает то, что для этого требуется два отдельных класса, что не так уж важно, за исключением того, что я чувствую, что это должно быть выполнимо только с одним классом.
Теперь у меня это как указано выше, но я пробовал два других способа, которые оба терпят неудачу (SceneBuilder не найдет и не позволит мне импортировать элементы управления), и я надеялся, что кто-нибудь скажет мне, почему, чтобы я мог продолжать свою жизнь.
Во втором случае я попытался использовать один класс, который расширил VBox и реализовал Initializable:
public class Hybrid extends VBox implements Initializable{ /*In this case the FXML file Controller would be set to this class.*/
/*FXML Variables Here*/
public Hybrid(){
this.Load();
}
private void Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("Hybrid.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch (IOException ex){
throw new IllegalStateException(ex);
}
assert this == loader.getController();
}
@Override public void initialize(URL location, ResourceBundle resources){
/*Initialization Stuff Here*/
}
}
Это имеет ИДЕАЛЬНЫЙ смысл для меня. Это ДОЛЖНО работать, по крайней мере, в моей голове, но это не так. JAR-файл прекрасно компилируется, и я даже готов поспорить, что он будет отлично работать в программе, но когда я пытаюсь импортировать .jar в Scene Builder, он не работает. Его нет в списке импортируемых элементов управления.
Итак... Я пробовал кое-что другое. Я попытался вложить класс Controller в класс Control:
public class Hybrid extends VBox{ /*In this case the FXML Controller I had set to Package.Hybrid.Controller*/
final private Controller ctrlr
public Hybrid(){
this.ctrlr = this.Load();
}
private Controller Load(){
/*Load Code*/
}
public class Controller implements Initializable{
/*Controller Code*/
}
}
Это тоже не сработало. Я пробовал публичный, приватный, общедоступный статический, приватный статический, ни один из них не работал.
Так почему же это так? Почему SceneBuilder не может распознать пользовательский элемент управления, если класс Control и класс Controller не являются двумя отдельными объектами?
РЕДАКТИРОВАТЬ:
Благодаря James_D ниже я смог получить ответ И заставить пользовательские элементы управления работать так, как мне хотелось бы. Я также смог создать общий метод Load, который работает для всех пользовательских классов, если имя класса совпадает с именем файла FXML:
private void Load(){
final FXMLLoader loader = new FXMLLoader();
String[] classes = this.getClass().getTypeName().split("\\.");
String loc = classes[classes.length - 1] + ".fxml";
loader.setRoot(this);
loader.setController(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource(loc));
try{
final Object root = loader.load();
assert root == this;
} catch (IOException ex){
throw new IllegalStateException(ex);
}
assert this == loader.getController();
}
Просто подумал, что поделюсь этим. Обратите внимание, что это работает только в том случае, если, например, ваш класс был назван Hybrid, а ваш файл FXML был назван Hybrid.fxml.