gpt4 book ai didi

java - 使用 JavaFx 应用 MVC

转载 作者:IT老高 更新时间:2023-10-28 21:21:09 27 4
gpt4 key购买 nike

我是 GUI 世界/OO 设计模式的新手,我想在我的 GUI 应用程序中使用 MVC 模式,我已经阅读了有关 MVC 模式的小教程,模型将包含数据, View 将包含视觉元素和 Controller 将绑定(bind)在 View 和模型之间。

我有一个包含 ListView 节点的 View ,并且 ListView 将填充来自人员类(模型)的名称。但我对一件事有点困惑。

我想知道的是,从文件中加载数据是 Controller 的责任还是模型的责任?以及名称的 ObservableList:应该存储在 Controller 中还是 Model 中?

最佳答案

这种模式有许多不同的变体。特别是,Web 应用程序上下文中的“MVC”与胖客户端(例如桌面)应用程序上下文中的“MVC”有些不同(因为 Web 应用程序必须位于请求-响应周期的顶部)。这只是使用 JavaFX 在胖客户端应用程序的上下文中实现 MVC 的一种方法。

您的Person类并不是真正的模型,除非您有一个非常简单的应用程序:这通常是我们所说的域对象,模型将包含对它的引用以及其他数据。在狭窄的环境中,例如当您只是考虑 ListView 时,你可以想到Person作为您的数据模型(它对 ListView 的每个元素中的数据进行建模),但在更广泛的应用程序上下文中,需要考虑更多的数据和状态。

如果您显示的是 ListView<Person>您需要的数据至少是 ObservableList<Person> .您可能还需要一个属性,例如 currentPerson ,这可能代表列表中的选定项。

如果您拥有的唯一 View 是ListView ,然后创建一个单独的类来存储这将是矫枉过正,但任何真正的应用程序通常会以多个 View 结束。此时,在模型中共享数据成为不同 Controller 相互通信的一种非常有用的方式。

所以,例如,你可能有这样的事情:

public class DataModel {

private final ObservableList<Person> personList = FXCollections.observableArrayList();

private final ObjectProperty<Person> currentPerson = new SimpleObjectPropery<>(null);

public ObjectProperty<Person> currentPersonProperty() {
return currentPerson ;
}

public final Person getCurrentPerson() {
return currentPerson().get();
}

public final void setCurrentPerson(Person person) {
currentPerson().set(person);
}

public ObservableList<Person> getPersonList() {
return personList ;
}
}

现在您可能有一个用于 ListView 的 Controller 显示如下:

public class ListController {

@FXML
private ListView<Person> listView ;

private DataModel model ;

public void initModel(DataModel model) {
// ensure model is only set once:
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}

this.model = model ;
listView.setItems(model.getPersonList());

listView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) ->
model.setCurrentPerson(newSelection));

model.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
listView.getSelectionModel().clearSelection();
} else {
listView.getSelectionModel().select(newPerson);
}
});
}
}

这个 Controller 本质上只是将列表中显示的数据绑定(bind)到模型中的数据,并保证模型的currentPerson始终是 ListView 中的选定项。

现在您可能有另一个 View ,例如编辑器,其中 firstName 具有三个文本字段。 , lastName , 和 email一个人的属性。它的 Controller 可能看起来像:

public class EditorController {

@FXML
private TextField firstNameField ;
@FXML
private TextField lastNameField ;
@FXML
private TextField emailField ;

private DataModel model ;

public void initModel(DataModel model) {
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
model.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (oldPerson != null) {
firstNameField.textProperty().unbindBidirectional(oldPerson.firstNameProperty());
lastNameField.textProperty().unbindBidirectional(oldPerson.lastNameProperty());
emailField.textProperty().unbindBidirectional(oldPerson.emailProperty());
}
if (newPerson == null) {
firstNameField.setText("");
lastNameField.setText("");
emailField.setText("");
} else {
firstNameField.textProperty().bindBidirectional(newPerson.firstNameProperty());
lastNameField.textProperty().bindBidirectional(newPerson.lastNameProperty());
emailField.textProperty().bindBidirectional(newPerson.emailProperty());
}
});
}
}

现在,如果您将这两个 Controller 设置为共享同一个模型,编辑器将编辑列表中当前选定的项目。

加载和保存数据应该通过模型来完成。有时,您甚至会将其分解到模型引用的单独类中(例如,允许您在基于文件的数据加载器和数据库数据加载器之间轻松切换,或者访问 Web 服务的实现)。在简单的情况下,您可能会这样做

public class DataModel {

// other code as before...

public void loadData(File file) throws IOException {

// load data from file and store in personList...

}

public void saveData(File file) throws IOException {

// save contents of personList to file ...
}
}

那么你可能有一个 Controller 来提供对这个功能的访问:

public class MenuController {

private DataModel model ;

@FXML
private MenuBar menuBar ;

public void initModel(DataModel model) {
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
}

@FXML
public void load() {
FileChooser chooser = new FileChooser();
File file = chooser.showOpenDialog(menuBar.getScene().getWindow());
if (file != null) {
try {
model.loadData(file);
} catch (IOException exc) {
// handle exception...
}
}
}

@FXML
public void save() {

// similar to load...

}
}

现在您可以轻松组装应用程序:

public class ContactApp extends Application {

@Override
public void start(Stage primaryStage) throws Exception {

BorderPane root = new BorderPane();
FXMLLoader listLoader = new FXMLLoader(getClass().getResource("list.fxml"));
root.setCenter(listLoader.load());
ListController listController = listLoader.getController();

FXMLLoader editorLoader = new FXMLLoader(getClass().getResource("editor.fxml"));
root.setRight(editorLoader.load());
EditorController editorController = editorLoader.getController();

FXMLLoader menuLoader = new FXMLLoader(getClass().getResource("menu.fxml"));
root.setTop(menuLoader.load());
MenuController menuController = menuLoader.getController();

DataModel model = new DataModel();
listController.initModel(model);
editorController.initModel(model);
menuController.initModel(model);

Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}

正如我所说,这种模式有很多变体(这可能更像是模型- View -演示者,或“被动 View ”变体),但这是一种方法(我基本上赞成这种方法)。通过构造函数向 Controller 提供模型更自然一些,但是使用 fx:controller 定义 Controller 类要困难得多。属性。这种模式也非常适合依赖注入(inject)框架。

更新:此示例的完整代码为 here .

如果您对 JavaFX 中的 MVC 教程感兴趣,请参阅:

关于java - 使用 JavaFx 应用 MVC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32342864/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com