ОК, это называется ответом с перерасходом, извините за это, но вот небольшой пример, который я привел, который пытается использовать простой шаблон MVC, чтобы сделать тривиальную вещь: нажмите кнопку и измените текст в JTextField. Это излишне, потому что вы можете сделать то же самое всего за несколько строк кода, но это иллюстрирует некоторые MVC в отдельных файлах и то, как модель контролирует состояние. Пожалуйста, задавайте вопросы, если что-то не так!
Основной класс, который собирает все вместе и начинает:
import javax.swing.*;
public class SwingMvcTest {
private static void createAndShowUI() {
// create the model/view/control and connect them together
MvcModel model = new MvcModel();
MvcView view = new MvcView(model);
MvcControl control = new MvcControl(model);
view.setGuiControl(control);
// EDIT: added menu capability
McvMenu menu = new McvMenu(control);
// create the GUI to display the view
JFrame frame = new JFrame("MVC");
frame.getContentPane().add(view.getMainPanel()); // add view here
frame.setJMenuBar(menu.getMenuBar()); // edit: added menu capability
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// call Swing code in a thread-safe manner per the tutorials
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Класс просмотра:
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
public class MvcView {
private MvcControl control;
private JTextField stateField = new JTextField(10);
private JPanel mainPanel = new JPanel(); // holds the main GUI and its components
public MvcView(MvcModel model) {
// add a property change listener to the model to listen and
// respond to changes in the model's state
model.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
// if the state change is the one we're interested in...
if (evt.getPropertyName().equals(MvcModel.STATE_PROP_NAME)) {
stateField.setText(evt.getNewValue().toString()); // show it in the GUI
}
}
});
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
// all the buttons do is call methods of the control
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.startButtonActionPerformed(e); // e.g., here
}
}
});
JButton endButton = new JButton("End");
endButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.endButtonActionPerformed(e); // e.g., and here
}
}
});
// make our GUI pretty
int gap = 10;
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, gap, 0));
buttonPanel.add(startButton);
buttonPanel.add(endButton);
JPanel statePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
statePanel.add(new JLabel("State:"));
statePanel.add(Box.createHorizontalStrut(gap));
statePanel.add(stateField);
mainPanel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
mainPanel.setLayout(new BorderLayout(gap, gap));
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(statePanel, BorderLayout.PAGE_END);
}
// set the control for this view
public void setGuiControl(MvcControl control) {
this.control = control;
}
// get the main gui and its components for display
public JComponent getMainPanel() {
return mainPanel;
}
}
Контроль:
import java.awt.event.ActionEvent;
public class MvcControl {
private MvcModel model;
public MvcControl(MvcModel model) {
this.model = model;
}
// all this simplistic control does is change the state of the model, that's it
public void startButtonActionPerformed(ActionEvent ae) {
model.setState(State.START);
}
public void endButtonActionPerformed(ActionEvent ae) {
model.setState(State.END);
}
}
Модель использует объект PropertyChangeSupport, чтобы позволить другим объектам (в данном случае представлению) прослушивать изменения состояния. Таким образом, модель в действительности является нашей «наблюдаемой», а точка зрения - «наблюдателем»
import java.beans.*;
public class MvcModel {
public static final String STATE_PROP_NAME = "State";
private PropertyChangeSupport pcSupport = new PropertyChangeSupport(this);
private State state = State.NO_STATE;
public void setState(State state) {
State oldState = this.state;
this.state = state;
// notify all listeners that the state property has changed
pcSupport.firePropertyChange(STATE_PROP_NAME, oldState, state);
}
public State getState() {
return state;
}
public String getStateText() {
return state.getText();
}
// allow addition of listeners or observers
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
}
Простое перечисление State для инкапсуляции понятия состояния:
public enum State {
NO_STATE("No State"), START("Start"), END("End");
private String text;
private State(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
public String getText() {
return text;
}
}
edit: я вижу, что вы также упомянули меню, поэтому я добавил поддержку меню с добавлением этого класса и добавлением нескольких строк в классе SwingMcvTest. Обратите внимание, что из-за разделения кода было легко внести это изменение в графический интерфейс, поскольку все, что нужно сделать в меню, - это вызвать методы управления. Не нужно ничего знать о модели или представлении:
import java.awt.event.ActionEvent;
import javax.swing.*;
public class McvMenu {
private JMenuBar menuBar = new JMenuBar();
private MvcControl control;
@SuppressWarnings("serial")
public McvMenu(MvcControl cntrl) {
this.control = cntrl;
JMenu menu = new JMenu("Change State");
menu.add(new JMenuItem(new AbstractAction("Start") {
public void actionPerformed(ActionEvent ae) {
if (control != null) {
control.startButtonActionPerformed(ae);
}
}
}));
menu.add(new JMenuItem(new AbstractAction("End") {
public void actionPerformed(ActionEvent ae) {
if (control != null) {
control.endButtonActionPerformed(ae);
}
}
}));
menuBar.add(menu);
}
public JMenuBar getMenuBar() {
return menuBar;
}
}
Боже, как много кода, чтобы сделать мелочь! Я назначаю себя и свой код для награды Rube Goldberg на стеке на этой неделе.