sbr*_*tla 73 java model-view-controller swing
我认为最难以真正掌握"真实SWING生活"的设计模式之一是MVC模式.我已经浏览了这个讨论模式的网站上的一些帖子,但我仍然觉得我不清楚如何利用我的(Java SWING)应用程序中的模式.
假设我有一个包含表格,几个文本字段和几个按钮的JFrame.我可能会使用TableModel将JTable与基础数据模型"桥接".但是,负责清除字段,验证字段,锁定字段以及按钮操作的所有函数通常都直接在JFrame中.但是,是不是混合了Controller和View的模式?
据我所见,我设法在查看JTable(和模型)时"正确"实现了MVC模式,但是当我整个看整个JFrame时,事情变得混乱.
我真的很想听听别人对此的看法.当你需要向用户显示表格,几个字段和一些按钮(使用MVC模式)时,你如何去做?
Dhr*_*ola 103
我强烈推荐给你的摇摆MVC的书是Freeman和Freeman的"Head First Design Patterns".他们对MVC有非常全面的解释.
简要总结
您是用户 - 您与视图进行交互.视图是模型的窗口.当您对视图执行某些操作(例如单击"播放"按钮)时,视图会告诉控制器您执行了哪些操作.这是控制器的工作.
控制器要求模型改变其状态.控制器采取您的行动并解释它们.如果单击某个按钮,控制器的工作就是弄清楚这意味着什么,以及如何根据该操作操纵模型.
控制器还可以要求视图改变.当控制器从视图接收到操作时,可能需要告知视图作为结果进行更改.例如,控制器可以启用或禁用界面中的某些按钮或菜单项.
模型在状态发生变化时通知视图.当模型中的某些内容发生变化时,根据您采取的某些操作(如单击按钮)或其他一些内部更改(如播放列表中的下一首歌曲已启动),模型会通知视图其状态已更改.
视图询问模型的状态.视图获取直接从模型显示的状态.例如,当模型通知视图新歌曲已开始播放时,视图从模型中请求歌曲名称并显示它.该视图还可能要求模型状态为控制器请求视图中的某些更改的结果.
来源(如果您想知道什么是"奶油控制器",想想一个奥利奥饼干,控制器是奶油中心,视图是顶部饼干,模型是底部饼干.)
嗯,如果你感兴趣的话,你可以从这里下载一首关于MVC模式的相当有趣的歌!
Swing编程可能遇到的一个问题涉及将SwingWorker和EventDispatch线程与MVC模式合并.根据您的程序,您的视图或控制器可能必须扩展SwingWorker并覆盖doInBackground()放置资源密集型逻辑的方法.这可以很容易地与典型的MVC模式融合,并且是Swing应用程序的典型特征.
编辑#1:
此外,将MVC视为各种模式的复合是很重要的.例如,您的模型可以使用Observer模式实现(需要将View注册为模型的观察者),而您的控制器可能使用策略模式.
编辑#2:
我还想特别回答你的问题.您应该在View中显示表按钮等,这显然会实现ActionListener.在您的actionPerformed()方法中,您检测事件并将其发送到控制器中的相关方法(请记住 - 视图包含对控制器的引用).因此,当单击按钮时,视图检测到事件,发送到控制器的方法,控制器可能会直接要求视图禁用按钮等.接下来,控制器将与模型交互并修改模型(其中大部分将具有getter和setter方法,以及一些其他方法来注册和通知观察者等等).一旦模型被修改,它将调用已注册观察者的更新(这将是您的情况下的视图).因此,视图现在将自行更新.
Bnr*_*rdo 34
我不喜欢视图是模型在数据更改时通知的视图.我会将该功能委托给控制器.在这种情况下,如果更改应用程序逻辑,则无需干扰视图的代码.视图的任务仅适用于应用程序组件+布局,仅此而已.在swing中进行布局已经是一项冗长的任务,为什么要让它干扰应用程序逻辑呢?
我对MVC(我目前正在使用,迄今为止如此优秀)的想法是:
风景 :
就像我说创建视图已经很冗长所以只需创建自己的实现:)
interface View{
JTextField getTxtFirstName();
JTextField getTxtLastName();
JTextField getTxtAddress();
}
Run Code Online (Sandbox Code Playgroud)
为了可测试性目的,将三者连接起来是理想的.我只提供了模型和控制器的实现.
该模型 :
public class MyImplementationOfModel implements Model{
...
private SwingPropertyChangeSupport propChangeFirer;
private String address;
private String firstName;
private String lastName;
public MyImplementationOfModel() {
propChangeFirer = new SwingPropertyChangeSupport(this);
}
public void addListener(PropertyChangeListener prop) {
propChangeFirer.addPropertyChangeListener(prop);
}
public void setAddress(String address){
String oldVal = this.address;
this.address = address;
//after executing this, the controller will be notified that the new address has been set. Its then the controller's
//task to decide what to do when the address in the model has changed. Ideally, the controller will update the view about this
propChangeFirer.firePropertyChange("address", oldVal, address);
}
...
//some other setters for other properties & code for database interaction
...
}
Run Code Online (Sandbox Code Playgroud)
控制者:
public class MyImplementationOfController implements PropertyChangeListener, Controller{
private View view;
private Model model;
public MyImplementationOfController(View view, Model model){
this.view = view;
this.model = model;
//register the controller as the listener of the model
this.model.addListener(this);
setUpViewEvents();
}
//code for setting the actions to be performed when the user interacts to the view.
private void setUpViewEvents(){
view.getBtnClear().setAction(new AbstractAction("Clear") {
@Override
public void actionPerformed(ActionEvent arg0) {
model.setFirstName("");
model.setLastName("");
model.setAddress("");
}
});
view.getBtnSave().setAction(new AbstractAction("Save") {
@Override
public void actionPerformed(ActionEvent arg0) {
...
//validate etc.
...
model.setFirstName(view.getTxtFName().getText());
model.setLastName(view.getTxtLName().getText());
model.setAddress(view.getTxtAddress().getText());
model.save();
}
});
}
public void propertyChange(PropertyChangeEvent evt){
String propName = evt.getPropertyName();
Object newVal = evt.getNewValue();
if("address".equalsIgnoreCase(propName)){
view.getTxtAddress().setText((String)newVal);
}
//else if property (name) that fired the change event is first name property
//else if property (name) that fired the change event is last name property
}
}
Run Code Online (Sandbox Code Playgroud)
设置MVC的Main:
public class Main{
public static void main(String[] args){
View view = new YourImplementationOfView();
Model model = new MyImplementationOfModel();
...
//create jframe
//frame.add(view.getUI());
...
//make sure the view and model is fully initialized before letting the controller control them.
Controller controller = new MyImplementationOfController(view, model);
...
//frame.setVisible(true);
...
}
}
Run Code Online (Sandbox Code Playgroud)
Ren*_*ink 22
MVC模式是如何构建用户界面的模型.因此它定义了3个元素Model,View,Controller:
例

当Button点击它调用ActionListener.该ActionListener只依赖于其他车型.它使用一些模型作为输入,其他模型作为结果或输出.它就像方法参数和返回值.模型在更新时通知ui.因此,控制器逻辑不需要知道ui组件.模型对象不知道ui.通知由观察者模式完成.因此,模型对象只知道有人想要在模型更改时收到通知.
在java swing中,有一些组件也实现了模型和控制器.例如javax.swing.Action.它实现了一个ui模型(属性:启用,小图标,名称等)并且是一个控制器,因为它扩展了ActionListener.
甲详细说明,示例应用程序和源代码:https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/.
MVC基础知识少于240行:
public class Main {
public static void main(String[] args) {
JFrame mainFrame = new JFrame("MVC example");
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainFrame.setSize(640, 300);
mainFrame.setLocationRelativeTo(null);
PersonService personService = new PersonServiceMock();
DefaultListModel searchResultListModel = new DefaultListModel();
DefaultListSelectionModel searchResultSelectionModel = new DefaultListSelectionModel();
searchResultSelectionModel
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Document searchInput = new PlainDocument();
PersonDetailsAction personDetailsAction = new PersonDetailsAction(
searchResultSelectionModel, searchResultListModel);
personDetailsAction.putValue(Action.NAME, "Person Details");
Action searchPersonAction = new SearchPersonAction(searchInput,
searchResultListModel, personService);
searchPersonAction.putValue(Action.NAME, "Search");
Container contentPane = mainFrame.getContentPane();
JPanel searchInputPanel = new JPanel();
searchInputPanel.setLayout(new BorderLayout());
JTextField searchField = new JTextField(searchInput, null, 0);
searchInputPanel.add(searchField, BorderLayout.CENTER);
searchField.addActionListener(searchPersonAction);
JButton searchButton = new JButton(searchPersonAction);
searchInputPanel.add(searchButton, BorderLayout.EAST);
JList searchResultList = new JList();
searchResultList.setModel(searchResultListModel);
searchResultList.setSelectionModel(searchResultSelectionModel);
JPanel searchResultPanel = new JPanel();
searchResultPanel.setLayout(new BorderLayout());
JScrollPane scrollableSearchResult = new JScrollPane(searchResultList);
searchResultPanel.add(scrollableSearchResult, BorderLayout.CENTER);
JPanel selectionOptionsPanel = new JPanel();
JButton showPersonDetailsButton = new JButton(personDetailsAction);
selectionOptionsPanel.add(showPersonDetailsButton);
contentPane.add(searchInputPanel, BorderLayout.NORTH);
contentPane.add(searchResultPanel, BorderLayout.CENTER);
contentPane.add(selectionOptionsPanel, BorderLayout.SOUTH);
mainFrame.setVisible(true);
}
}
class PersonDetailsAction extends AbstractAction {
private static final long serialVersionUID = -8816163868526676625L;
private ListSelectionModel personSelectionModel;
private DefaultListModel personListModel;
public PersonDetailsAction(ListSelectionModel personSelectionModel,
DefaultListModel personListModel) {
boolean unsupportedSelectionMode = personSelectionModel
.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
if (unsupportedSelectionMode) {
throw new IllegalArgumentException(
"PersonDetailAction can only handle single list selections. "
+ "Please set the list selection mode to ListSelectionModel.SINGLE_SELECTION");
}
this.personSelectionModel = personSelectionModel;
this.personListModel = personListModel;
personSelectionModel
.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel listSelectionModel = (ListSelectionModel) e
.getSource();
updateEnablement(listSelectionModel);
}
});
updateEnablement(personSelectionModel);
}
public void actionPerformed(ActionEvent e) {
int selectionIndex = personSelectionModel.getMinSelectionIndex();
PersonElementModel personElementModel = (PersonElementModel) personListModel
.get(selectionIndex);
Person person = personElementModel.getPerson();
String personDetials = createPersonDetails(person);
JOptionPane.showMessageDialog(null, personDetials);
}
private String createPersonDetails(Person person) {
return person.getId() + ": " + person.getFirstName() + " "
+ person.getLastName();
}
private void updateEnablement(ListSelectionModel listSelectionModel) {
boolean emptySelection = listSelectionModel.isSelectionEmpty();
setEnabled(!emptySelection);
}
}
class SearchPersonAction extends AbstractAction {
private static final long serialVersionUID = 4083406832930707444L;
private Document searchInput;
private DefaultListModel searchResult;
private PersonService personService;
public SearchPersonAction(Document searchInput,
DefaultListModel searchResult, PersonService personService) {
this.searchInput = searchInput;
this.searchResult = searchResult;
this.personService = personService;
}
public void actionPerformed(ActionEvent e) {
String searchString = getSearchString();
List<Person> matchedPersons = personService.searchPersons(searchString);
searchResult.clear();
for (Person person : matchedPersons) {
Object elementModel = new PersonElementModel(person);
searchResult.addElement(elementModel);
}
}
private String getSearchString() {
try {
return searchInput.getText(0, searchInput.getLength());
} catch (BadLocationException e) {
return null;
}
}
}
class PersonElementModel {
private Person person;
public PersonElementModel(Person person) {
this.person = person;
}
public Person getPerson() {
return person;
}
@Override
public String toString() {
return person.getFirstName() + ", " + person.getLastName();
}
}
interface PersonService {
List<Person> searchPersons(String searchString);
}
class Person {
private int id;
private String firstName;
private String lastName;
public Person(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public int getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
class PersonServiceMock implements PersonService {
private List<Person> personDB;
public PersonServiceMock() {
personDB = new ArrayList<Person>();
personDB.add(new Person(1, "Graham", "Parrish"));
personDB.add(new Person(2, "Daniel", "Hendrix"));
personDB.add(new Person(3, "Rachel", "Holman"));
personDB.add(new Person(4, "Sarah", "Todd"));
personDB.add(new Person(5, "Talon", "Wolf"));
personDB.add(new Person(6, "Josephine", "Dunn"));
personDB.add(new Person(7, "Benjamin", "Hebert"));
personDB.add(new Person(8, "Lacota", "Browning "));
personDB.add(new Person(9, "Sydney", "Ayers"));
personDB.add(new Person(10, "Dustin", "Stephens"));
personDB.add(new Person(11, "Cara", "Moss"));
personDB.add(new Person(12, "Teegan", "Dillard"));
personDB.add(new Person(13, "Dai", "Yates"));
personDB.add(new Person(14, "Nora", "Garza"));
}
public List<Person> searchPersons(String searchString) {
List<Person> matches = new ArrayList<Person>();
if (searchString == null) {
return matches;
}
for (Person person : personDB) {
if (person.getFirstName().contains(searchString)
|| person.getLastName().contains(searchString)) {
matches.add(person);
}
}
return matches;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
61320 次 |
| 最近记录: |