我想对自定义 JFX 组件进行子类化以更改/扩展它们的行为。作为一个真实的例子,我想扩展一个具有编辑功能的数据查看器组件。
考虑以下非常小的场景。使用该课程Super
非常有效。但是当实例化子类Sub
(在 FXML 文件中)时,FXMLLoader
不再注入该@FXML
字段label
。因此,调用initialize
会导致 aNullPointerException
访问具有 value 的字段null
。我想FXMLLoader
不知何故需要这些信息来初始化使用Super.fxmlSuper
的子对象。Sub
请注意,该方法会在注入后initialize
自动调用。FXMLLoader
我知道将超级组件嵌套在子组件中应该可以正常工作,但我仍然想知道这是否可以使用继承。
扩大label
to的知名度protected
显然并没有解决这个问题。fx:root
结合(此处@DefaultProperty
已提出此解决方案)定义扩展点均无效。
我很感激任何帮助。
fxml/超级.fxml
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.*?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="HBox">
<Label fx:id="label"/>
</fx:root>
超级java
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class Super extends HBox {
@FXML
protected Label label;
public Super() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/" + getClass().getSimpleName() + ".fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void initialize() {
label.setText("Super");
}
}
fxml/Sub.fxml
<?import test.Super?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="Super"></fx:root>
子.java
public class Sub extends Super {
public Sub() {
super();
}
}
更新
就像在这个问题中一样,要走的路似乎是FXMLLoader
为每个继承级别调用(附加了一个 FXML 文件)。问题归结为注入@FXML
-annotated 字段,然后连接到调用initialize
。意思是,如果我们希望这些字段被注入,initialize
之后会为每个 single 调用load
。但是当initialize
被每个子类覆盖时,最具体的实现会被调用n
次数(n
继承级别的数量在哪里)。
就像是
public void initialize() {
if (getClass() == THISCLASS) {
realInitialize();
}
}
[Update]不会[/Update]解决这个问题,但对我来说似乎是一个 hack。
考虑@mrak 的这个演示代码,它显示了每个继承级别的加载。当我们initialize
在两个级别中实现方法时,就会出现上述问题。
这是一个基于mraks 代码的更完整的最小工作示例。
超级java
package test;
import java.io.IOException;
import java.net.URL;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class Super extends HBox {
@FXML
private Label label;
public Super() {
super();
loadFxml(Super.class.getResource("/fxml/Super.fxml"), this, Super.class);
}
public void initialize() {
label.setText("initialized");
}
protected static void loadFxml(URL fxmlFile, Object rootController, Class<?> clazz) {
FXMLLoader loader = new FXMLLoader(fxmlFile);
if (clazz == rootController.getClass()) { // PROBLEM
loader.setController(rootController);
}
loader.setRoot(rootController);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
}
子.java
package test;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class Sub extends Super {
@FXML
private Button button;
public Sub() {
super();
loadFxml(Sub.class.getResource("/fxml/Sub.fxml"), this, Sub.class);
}
@Override
public void initialize() {
super.initialize();
button.setText("initialized");
}
}
超级.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.*?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="HBox">
<Label fx:id="label" text="not initialized"/>
</fx:root>
子.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import test.Super?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="Super">
<Button fx:id="button" text="not initialized"/>
</fx:root>
请参阅 中的注释行Super.loadFxml
。使用此条件会导致仅注入@FXML
叶中的条目。但initialize
只被调用一次。不使用此条件会导致(理论上)注入所有@FXML
条目。但是initialize
在每次加载后NullPointerException
发生,因此 s 在每次非叶初始化时发生。
initialize
当根本不使用并自己调用一些init函数时,可以解决该问题。但同样,这对我来说似乎很老套。