JavaFX UI使用FXML控制架构(Control + Skin)

时间:2014-06-13 15:25:32

标签: java model-view-controller custom-controls javafx-8 fxml

在JavaFX8中,有一个UI Controls Architecture用于制作自定义控件。基本上是基于:

  • 控制。
  • 皮肤。
  • CSS。

此外,还有basic structure of an FXML project用于制作GUI。基本上:

  • 控制。
  • FXML文件。
  • CSS。

我想将FXML与UI Controls Architecture一起使用,所以我的问题是:

谁是FXML文件的控制器?皮肤?

我必须做以下代码吗?:

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);
    }
}

2 个答案:

答案 0 :(得分:4)

我认为你是在正确的轨道上,Skin类是加载的FXML文件的控制器,因为Skin负责定义构成'look'的节点一个特定的控制。

Control类本身应该只定义保存控件状态的属性,而不应该关心Skin实际创建视图层次结构的方式(也就是说,它应该只关心它的状态,而不是它的样子。)

我要做的一个区别是将fxmlloader.setController(control);更改为fxmlloader.setController(this);,以便Skin类成为控制器而不是控件本身。

您可以做的另一件事是将FXMLLoader逻辑移动到基类中,这样您每次要创建Skin时都不必复制它,如下所示:< / p>

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();
}

我有一个JavaFX UserControl on my Github页面,它与上面的FXMLSkinBase类非常相似。它使用约定来加载与派生类同名的FXML文件,以便每次都不需要指定FXML文件名。 I.E.如果派生的皮肤被称为FooControlSkin,控件将自动加载名为FooControlSkin.fxml的FXML文件。

该类非常简单,代码可以很容易地重构为一个满足您要求的功能齐全的FXMLSkinBase类。

答案 1 :(得分:0)

我更好的方法:

  • 无需使用<fx:root>作为.fxml文件的根元素。
  • 无需致电fxmlLoader.setRoot(control);
  • 无需致电fxmlLoader.setController(control);
  • 允许皮肤通过FXML根元素中的fx:controller="{controller class name}"自动成为控制器。
  • 允许IDE在ControlNameSkin类中突出显示对FXML的虚假@FXML引用。

适当的Control类。

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);
        }
    }
}

Skin和Controller的组合类。

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);
            }
        }
    }
}