java - 从 Controller 访问域对象

标签 java model-view-controller javafx javafx-8 fxml

根据this文档 FXML 可以被视为 MVC 的“ View ”部分。域对象在 Java 端(“模型”)定义。我找不到“M”和“C”之间的连接 - 我想从 Controller 修改一些域对象(或启动修改):“M”<-“C”

但是这样的代码与我调用 FXMLLoader.load() 的位置没有关系:

public class FXMLTableViewController {
    @FXML private TableView<Person> tableView;
    @FXML private TextField firstNameField;
    @FXML private TextField lastNameField;
    @FXML private TextField emailField;

    @FXML
    protected void addPerson(ActionEvent event) {
        ObservableList<Person> data = tableView.getItems();
        data.add(new Person(firstNameField.getText(),
            lastNameField.getText(),
            emailField.getText()
        ));

        firstNameField.setText("");
        lastNameField.setText("");
        emailField.setText("");
    }
}

此代码与应用程序的其余代码完全分离。建立这种连接的正确方法是什么?

最佳答案

不要使用静态 FXMLLoader.load(URL)方法。相反,创建一个 FXMLLoader实例。然后你可以自己实例化 Controller 并调用 setController(...) ,或者您可以设置 Controller 工厂。

使用 setController(...)

所以假设你有一些模型类,将其命名为 Model 。定义您的 Controller 以引用它:

public class MyController {
    private final Model model ;

    // usual @FXML-annotated fields, etc

    public MyController(Model model) {
        this.model = model ;
    }

    public void initialize() { ... }

    // handler methods, etc...
}

现在,删除 fx:controller FXML 文件中的属性,而是执行以下操作:

final Model model = new Model();

FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("path/to/fxml/file.fxml"));
loader.setController(new MyController(model));
Parent root = loader.<Parent>load();

使用 setControllerFactory(...)

如果您想要或需要使用fx:controller属性(例如,如果您在 FXML 中使用 <fx:include ...> 标签并注入(inject)嵌套 Controller ,这可能也需要访问模型),您可以指定一个 Controller 工厂,它实际上是一个将 Controller 类型映射到的函数 Controller 实例。 FXMLLoader将使用它来确定如何从您在 fx:controller 中指定的类名创建对象属性。

例如:

final Model model = new Model();

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
loader.setControllerFactory(new Callback<Class<?>, Object>() {
    @Override
    public Object call(Class<?> type) {
        try {
             for (Constructor<?> constructor : type.getConstructors()) {
                 if (constructor.getParameterCount()==1 && 
                         constructor.getParameterTypes()[0]==Model.class) {
                     return constructor.newInstance(model);
                  }
             }
             // no matching constructor found, just call no-arg constructor as default:
             return type.newInstance();
        } catch (Exception exc) {
            exc.printStackTrace();
            return null ; // bail...
        }
    }
});
Parent root = loader.<Parent>load();

请注意,在此版本中:

  1. 包含的任何 FXML 文件都将使用相同的 Controller 工厂,因此如果它们的 Controller 具有采用 Model 类型的单个参数的构造函数, ,他们将收到对相同 Model 的引用实例
  2. 如果您使用多个FXMLLoader在您的应用程序中,您可以重用相同的 Controller 工厂来传递相同的 Model实例到加载器,因此所有 Controller 都可以访问相同的 Model实例。

关于 Controller 工厂的其他想法

Controller 工厂是一个非常强大且灵活的机制。例如,定义一个简单地遵循 Spring ApplicationContext 的 Controller 工厂是非常容易的。 。这样您就可以为 Controller 定义接口(interface),并且只需在 FXML 文件中指定接口(interface)名称即可。然后,您的 Spring 配置文件可以确定要使用的 Controller 接口(interface)的实现,当然也可以将模型(和域对象)注入(inject)到 Controller 中。

另请参阅

如果您经常这样做,请查看 Adam Bien's afterburner framework 。 Adam 定义了一个可重用的 Controller 工厂,用于加载 Controller 并检查 @Inject其中的注释字段,并将单例实例注入(inject)到这些字段中。这提供了很大的灵 active ,因为您可以轻松地向 Controller 添加更多共享资源。

关于java - 从 Controller 访问域对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23414737/

相关文章:

javascript - 获取 json 数据在 IE 中有效,但在 FF 和 Chrome 中无效

JavaFX如何改变阶段

java - MVC : Is the View allowed to reference mutable elements of the Model without modifying them?

JavaFX AnimationTimer 未正确停止

JavaFX 从切换按钮获取坐标

java - 安卓按钮:

java - 如何使 JTextField 的一部分不可编辑

java - 如何在 Java 中进行延迟?

java - 启动后的 Android Studio 错误

ruby-on-rails - 如何使用 Ruby on Rails 将数据从 Controller 传递到模型?