- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我希望有人能够向我解释为什么 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 并实现可初始化:
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*/
}
}
这也不起作用。我尝试了公共(public)、私有(private)、公共(public)静态、私有(private)静态,但都不起作用。
那么为什么会出现这种情况呢?为什么 SceneBuilder 无法识别自定义控件,除非 Control 类和 Controller 类是两个独立的实体?
感谢下面的 James_D,我能够得到答案并使自定义控件按照我想要的方式工作。如果类的名称与 FXML 文件的名称相同,我还能够创建适用于所有自定义类的通用 Load 方法:
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 时才有效。
最佳答案
您的第二个(和第三个)版本根本无法工作(SceneBuilder 或没有 SceneBuilder),因为断言
this == loader.getController()
将会失败。当您调用 loader.load()
时,FXMLLoader
会看到 fx:controller="some.package.Hybrid"
并创建它的一个新实例。现在您有了 Hybrid
类的两个实例:一个在 FXMLLoader
上调用 load
,另一个被设置为加载的 FXML 的 Controller 。
您需要从 FXML 文件中删除 fx:controller
属性,并直接在代码中设置 Controller ,如 documentation 中所示。 :
private void Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("Hybrid.fxml"));
// add this line, and remove the fx:controller attribute from the fxml file:
loader.setController(this);
try{
final Object root = loader.load();
assert root == this;
} catch (IOException ex){
throw new IllegalStateException(ex);
}
assert this == loader.getController();
}
使用 SceneBuilder 进行实验,它似乎会尝试通过调用无参数构造函数来创建自定义控件,并期望在不创建异常的情况下完成。在这种特定情况下,它似乎无法正确处理注入(inject) @FXML
带注释的值。我建议在 jira 提交错误为了这。
作为解决方法,您可能必须编写代码,以便即使未注入(inject) @FXML
注释字段,也能完成无参数构造函数的执行,而不会引发异常。 (是的,这很痛苦。)
关于java - 为什么 SceneBuilder 对自定义控件如此讲究?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26243900/
我是一名优秀的程序员,十分优秀!