Существует множество различных вариантов этого рисунка. В частности, «MVC» в контексте веб-приложения интерпретируется несколько иначе, чем «MVC» в контексте приложения с толстым клиентом (например, настольного) (поскольку веб-приложение должно сидеть поверх цикла запроса-ответа). Это всего лишь один подход к реализации MVC в контексте толстого клиентского приложения с использованием JavaFX.
Ваш класс Person
на самом деле не является моделью, если у вас нет очень простого приложения: это обычно то, что мы называем объектом домена, и модель будет содержать ссылки на нее вместе с другими данными. В узком контексте, например, когда вы только, думая о ListView
, вы можете думать о Person
как о своей модели данных (она моделирует данные в каждом элементе ListView
), но в более широком контексте приложения, есть больше данных и состояния для рассмотрения.
Если у вас есть ListView<Person>
, данные необходимы, как минимум, ObservableList<Person>
. Вам также может понадобиться свойство, такое как currentPerson
, которое может представлять выбранный элемент в списке.
Если вид только у вас есть это ListView
, а затем создать отдельный класс для хранения это было бы излишним, но любое реальное применение, как правило, в конечном итоге с несколькими видами. На данный момент использование данных, совместно используемых в модели, становится очень полезным способом для взаимодействия разных контроллеров друг с другом.
Так, например, вы могли бы иметь что-то вроде этого:
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
дисплея, который выглядит следующим образом:
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);
}
});
}
}
Этот контроллер в основном только зываетс данные, отображаемые в списке, в данные модели, и гарантирует, что модель currentPerson
всегда является выбранным элементом в представлении списка.
Теперь у вас может быть другой вид, скажем, редактора, с тремя текстовыми полями для firstName
, lastName
и email
свойствами человека. Это контроллер может выглядеть следующим образом:
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());
}
});
}
}
Теперь, если вы установите вещи так, как эти контроллеры разделяют ту же модель, редактор будет редактировать выбранный элемент в списке.
Загрузка и сохранение данных должны выполняться с помощью модели.Иногда вы даже учитываете это в отдельном классе, к которому у модели есть ссылка (что позволяет легко переключаться между файловым загрузчиком на основе файлов и загрузчиком данных базы данных или, например, реализацией, которая обращается к веб-службе). В простом случае вы можете сделать
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 ...
}
}
Тогда вы, возможно, есть контроллер, который обеспечивает доступ к этой функции:
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();
}
}
As Я сказал, что существует множество вариаций этого шаблона (и это, вероятно, больше вариант модели-представления-презентатора или «пассивного представления»), но это один из подходов (один из которых я в основном предпочитаю). Немного более естественно представить модель контроллерам через их конструктор, но тогда гораздо сложнее определить класс контроллера с атрибутом fx:controller
. Этот шаблон также сильно подкрепляется рамками внедрения инъекций.
Обновление: Полный код для этого примера - here.
Этот ПАК был проницательным http://stackoverflow.com/questions/23187932/mvc-with-javafx?rq=1 – Cobusve
Возможно, вы захотите взглянуть на шаблон MVVM, который является вариацией шаблона MVC, который работает очень хорошо с JavaFX imho. – findusl