JavaFXUI使用FXML控制体系结构(控制和皮肤)
在JavaFX8中,有一个用于生成自定义控件的。基本上基于:JavaFXUI使用FXML控制体系结构(控制和皮肤),java,model-view-controller,custom-controls,javafx-8,fxml,Java,Model View Controller,Custom Controls,Javafx 8,Fxml,在JavaFX8中,有一个用于生成自定义控件的。基本上基于: 控制 皮肤 CSS 此外,还有一个用于制作GUI的。基本上: 控制 FXML文件 CSS 我想将FXML与一起使用,所以我的问题是: 谁是FXML文件的控制器?皮肤 我必须执行以下类似代码的操作: public class MySkin extends SkinBase<MyControl> { public GaugeSkin(MyControl control) { super(control);
- 控制
- 皮肤李>
- CSS
- 控制
- FXML文件
- CSS
public class MySkin extends SkinBase<MyControl> {
public GaugeSkin(MyControl control) {
super(control);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MyView.fxml"));
fxmlLoader.setRoot(control);
fxmlLoader.setController(control);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
公共类MySkin扩展了SkinBase{
公共计量皮肤(MyControl控制){
超级(控制);
FXMLLoader FXMLLoader=新的FXMLLoader(getClass().getResource(“MyView.fxml”);
fxmlLoader.setRoot(控制);
fxmlLoader.setController(控制);
试一试{
fxmloader.load();
}捕获(IOException异常){
抛出新的RuntimeException(异常);
}
}
我认为您使用Skin
类作为加载的FXML文件的控制器是正确的,因为正是Skin
负责定义构成特定控件“外观”的节点
控件
类本身实际上应该只定义保存控件状态的属性,而不应该关心外观
如何实际创建视图层次结构(也就是说,它应该只关心它的状态,而不关心它的外观)
我要做的一个区别是将fxmloader.setController(control);
更改为fxmloader.setController(this);
,这样Skin
类就变成了控制器而不是控件本身
您可以做的另一件事是将fxmloader
逻辑移动到基类中,这样您就不必在每次创建皮肤时都复制它,类似这样:
public abstract class FXMLSkin<C extends Control> extends SkinBase<C>{
public FXMLSkin(C control) {
super(control);
this.load();
}
private void load() {
FXMLLoader loader = new FXMLLoader(getFXML());
loader.setController(this);
try {
Node root = loader.load();
this.getChildren().add(root);
} catch (IOException ex) {
Logger.getLogger(FXMLSkin.class.getName()).log(Level.SEVERE, null, ex);
}
}
protected abstract URL getFXML();
}
公共抽象类FXMLSkin扩展了SkinBase{
公共FXMLSkin(C控制){
超级(控制);
这个.load();
}
专用空心荷载(){
FXMLLoader=新的FXMLLoader(getFXML());
loader.setController(此);
试一试{
节点根=loader.load();
this.getChildren().add(根目录);
}捕获(IOEX异常){
Logger.getLogger(FXMLSkin.class.getName()).log(Level.SEVERE,null,ex);
}
}
受保护的抽象URL getFXML();
}
我有一个页面,它的功能与上面的FXMLSkinBase
类非常相似。它使用约定加载与派生类同名的FXML文件,这样就不需要每次都指定FXML文件名。例如,如果您的派生皮肤名为FooControlSkin
,则控件将自动加载一个名为FooControlSkin.FXML
的FXML文件
该类非常简单,代码可以很容易地重构为功能齐全的FXMLSkinBase
类,以满足您的需求。我的更好方法:
- 不再需要将
作为.fxml文件的根元素
- 正在删除调用
fxmloader.setRoot(控制);
的需要
- 删除调用
fxmlLoader.setController(控制);
的需要
- 允许皮肤通过FXML根元素中的
fx:controller=“{controller class name}”
自动成为控制器
- 允许IDE突出显示ControlNameSkin类中对FXML的伪@FXML引用
一个适当的控制类
public static class ControlName extends Control {
@Override
protected Skin<?> createDefaultSkin() {
ControlNameSkin.Factory factory = new ControlNameSkin.Factory(this);
FXMLLoader fxmlLoader = new FXMLLoader(getClass()
.getResource("ControlName.fxml"));
fxmlLoader.setControllerFactory(factory);
try {
Node root = fxmlLoader.load();
ControlNameSkin skin = fxmlLoader.getController();
skin.construct(root);
return skin;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
公共静态类ControlName扩展控件{
@凌驾
受保护的皮肤createDefaultSkin(){
ControlNameSkin.Factory工厂=新的ControlNameSkin.Factory(此);
FXMLLoader FXMLLoader=新的FXMLLoader(getClass()
.getResource(“ControlName.fxml”);
fxmlLoader.setControllerFactory(工厂);
试一试{
节点根=fxmloader.load();
ControlNameSkin skin=fxmlLoader.getController();
皮肤构造(根);
返回皮肤;
}捕获(IOEX异常){
抛出新的运行时异常(ex);
}
}
}
一个组合的皮肤和控制器类
public static class ControlNameSkin extends SkinBase<ControlName> {
public ControlNameSkin(Factory factory) {
super(factory.control);
// any setup NOT using FXML references
}
public void construct(Node root) {
ControlName control = getSkinnable();
// any setup using FXML references
getChildren().add(root);
}
public static class Factory implements Callback<Class<?>, Object> {
public final ControlName control;
public Factory(ControlName control) {
this.control = control;
}
@Override
public Object call(Class<?> cls) {
try {
return cls.getConstructor(Factory.class).newInstance(this);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
}
public静态类ControlNameSkin扩展SkinBase{
公共控制(工厂){
超级(工厂控制);
//任何不使用FXML引用的设置
}
公共void构造(节点根){
ControlName控件=getSkinnable();
//使用FXML引用的任何设置
getChildren().add(根目录);
}
公共静态类工厂实现回调这个答案的问题是,您必须使用setController(.),因此扔掉在FXML根元素中使用fx:controller属性所获得的IDE验证优势;这可能会导致意外的运行时绑定错误或NullPointerException,因为我发现了困难的方法!