【问题标题】:JavaFX UI Controls Architecture (Control+Skin) with FXML带有 FXML 的 JavaFX UI 控件架构(控件+皮肤)
【发布时间】:2014-08-04 05:24:24
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    我认为你在正确的轨道上 Skin 类是加载的 FXML 文件的控制器,因为它是 Skin 负责定义构成特定控件“外观”的节点.

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

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

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

    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 文件名。 IE。如果您的派生皮肤名为FooControlSkin,则控件将自动加载名为FooControlSkin.fxml 的FXML 文件。

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

    【讨论】:

    • 这个答案的问题是你必须使用setController(.),所以放弃在FXML根元素中使用fx:controller属性获得的IDE验证优势;这可能会导致意外的运行时绑定错误或 NullPointerExceptions,因为我发现很难!
    【解决方案2】:

    我更好的方法:

    • 不再需要 &lt;fx:root&gt; 作为 .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);
            }
        }
    }
    

    一个组合的皮肤和控制器类。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多