如何在Java Swing应用程序中管理控制器的视图更新

我发现用Swing编写好的OO代码非常难。 我的问题基本上是我有一个有动作监听器的视图(JPanel)。 动作侦听器确定单击了哪个按钮并调用适当的控制器方法。 问题是此控制器方法需要更新另一个视图。 所以我遇到的问题是我将视图传递给控制器​​。 这是一个例子。

public class MyView extends JPanel implements ActionListener { private final MyController controller = new MyController(); @Override public void actionPerformed(ActionEvent e) { this.controller.updateOtherView(); } } 

这基本上就是我想要的,但这就是最终发生的事情。

 public class MyView extends JPanel implements ActionListener { private MyController controller = new MyController(); private OtherView otherView; public MyView(MyOtherView otherView) { this.otherView = otherView; } @Override public void actionPerformed(ActionEvent e) { this.controller.updateOtherView(otherView); } } 

您可以看到,随着需要更新的视图数量的增加以及看起来像这样的类的数量增加,视图本质上是全局变量,代码变得复杂且不清楚。 我遇到的另一个问题是这个其他视图通常不直接传递到MyView,但它必须经过MyView的父级才能进入MyView,这真的只是让我烦恼。

对于一个真实的例子,假设我有一个菜单和这个MyView。 MyView有一个播放按钮,播放一段时间的音乐,它会禁用(灰显)播放按钮,直到音乐结束。 如果我有一个名为play的菜单选项,现在我需要访问其他视图播放按钮,这样我就可以将它变灰了。 如果没有这种恼人的意见传递,我怎能做到这一点呢? 虽然可能有针对此问题的特定解决方案,但我正在寻找能够解决一般情况下此视图访问问题的方法。

我根本不确定如何解决这个问题。 我现在正在使用MVC模式术语而不使用MVC模式,这可能是必要的,也可能是不必要的。 任何帮助表示赞赏。

一种解决方案:只需让控制器更新模型即可。 然后附加到模型的侦听器将更新视图。 您还可以让JMenuItems和相应的JButton共享相同的Action,因此当您禁用Action时,它将禁用使用该Action的所有按钮/菜单/等。

例如,主类:

 import javax.swing.JFrame; import javax.swing.SwingUtilities; public class MvcExample { private static void createAndShowGui() { MyView view = new MyView(); MyMenuBar menuBar = new MyMenuBar(); MyModel model = new MyModel(); MyControl control = new MyControl(model); control.addProgressMonitor(view); control.addView(view); control.addView(menuBar); model.setState(MyState.STOP); JFrame frame = new JFrame("MVC Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(view.getMainPanel()); frame.setJMenuBar(menuBar.getMenuBar()); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } private static final byte[] DATA_ARRAY = { 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x46, 0x75, 0x62, 0x61, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x30, 0x36, 0x2f, 0x31, 0x36, 0x2f, 0x32, 0x30, 0x31, 0x32, 0x2e, 0x20, 0x46, 0x75, 0x62, 0x61, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x21 }; } 

控制:

 import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; @SuppressWarnings("serial") public class MyControl { private MyModel model; private PlayAction playAction = new PlayAction(); private PauseAction pauseAction = new PauseAction(); private StopAction stopAction = new StopAction(); private List progMonitorList = new ArrayList(); public MyControl(MyModel model) { this.model = model; model.addPropertyChangeListener(new MyPropChangeListener()); } public void addProgressMonitor(MyProgressMonitor progMonitor) { progMonitorList.add(progMonitor); } public void addView(MySetActions setActions) { setActions.setPlayAction(playAction); setActions.setPauseAction(pauseAction); setActions.setStopAction(stopAction); } private class MyPropChangeListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent pcEvt) { if (MyState.class.getName().equals(pcEvt.getPropertyName())) { MyState state = (MyState) pcEvt.getNewValue(); if (state == MyState.PLAY) { playAction.setEnabled(false); pauseAction.setEnabled(true); stopAction.setEnabled(true); } else if (state == MyState.PAUSE) { playAction.setEnabled(true); pauseAction.setEnabled(false); stopAction.setEnabled(true); } else if (state == MyState.STOP) { playAction.setEnabled(true); pauseAction.setEnabled(false); stopAction.setEnabled(false); } } if (MyModel.PROGRESS.equals(pcEvt.getPropertyName())) { for (MyProgressMonitor progMonitor : progMonitorList) { int progress = (Integer) pcEvt.getNewValue(); progMonitor.setProgress(progress); } } } } private class PlayAction extends AbstractAction { public PlayAction() { super("Play"); putValue(MNEMONIC_KEY, KeyEvent.VK_P); } @Override public void actionPerformed(ActionEvent e) { model.play(); } } private class StopAction extends AbstractAction { public StopAction() { super("Stop"); putValue(MNEMONIC_KEY, KeyEvent.VK_S); } @Override public void actionPerformed(ActionEvent e) { model.stop(); } } private class PauseAction extends AbstractAction { public PauseAction() { super("Pause"); putValue(MNEMONIC_KEY, KeyEvent.VK_A); } @Override public void actionPerformed(ActionEvent e) { model.pause(); } } } 

国家枚举:

 public enum MyState { PLAY, STOP, PAUSE } 

其中一个视图界面:

 import javax.swing.Action; public interface MySetActions { void setPlayAction(Action playAction); void setPauseAction(Action pauseAction); void setStopAction(Action stopAction); } 

另一个视图界面:

 public interface MyProgressMonitor { void setProgress(int progress); } 

主GUI视图:

 import java.awt.BorderLayout; import java.awt.GridLayout; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JProgressBar; public class MyView implements MySetActions, MyProgressMonitor { private JButton playButton = new JButton(); private JButton stopButton = new JButton(); private JButton pauseButton = new JButton(); private JPanel mainPanel = new JPanel(); private JProgressBar progressBar = new JProgressBar(); public MyView() { progressBar.setBorderPainted(true); JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0)); btnPanel.add(playButton); btnPanel.add(pauseButton); btnPanel.add(stopButton); mainPanel.setLayout(new BorderLayout(0, 5)); mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15)); mainPanel.add(btnPanel, BorderLayout.CENTER); mainPanel.add(progressBar, BorderLayout.PAGE_END); } @Override public void setPlayAction(Action playAction) { playButton.setAction(playAction); } @Override public void setStopAction(Action stopAction) { stopButton.setAction(stopAction); } @Override public void setPauseAction(Action pauseAction) { pauseButton.setAction(pauseAction); } @Override public void setProgress(int progress) { progressBar.setValue(progress); } public JComponent getMainPanel() { return mainPanel; } } 

View的菜单栏部分:

 import java.awt.event.KeyEvent; import javax.swing.Action; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; public class MyMenuBar implements MySetActions { private JMenuItem playMenItem = new JMenuItem(); private JMenuItem pauseMenuItem = new JMenuItem(); private JMenuItem stopMenItem = new JMenuItem(); private JMenuBar menuBar = new JMenuBar(); public MyMenuBar() { JMenu menu = new JMenu("Main Menu"); menu.setMnemonic(KeyEvent.VK_M); menu.add(playMenItem); menu.add(pauseMenuItem); menu.add(stopMenItem); menuBar.add(menu); } public JMenuBar getMenuBar() { return menuBar; } @Override public void setPlayAction(Action playAction) { playMenItem.setAction(playAction); } @Override public void setStopAction(Action stopAction) { stopMenItem.setAction(stopAction); } @Override public void setPauseAction(Action pauseAction) { pauseMenuItem.setAction(pauseAction); } } 

最后,模型:

 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import javax.swing.Timer; import javax.swing.event.SwingPropertyChangeSupport; public class MyModel { public final static String PROGRESS = "progress"; protected static final int MAX_PROGRESS = 100; private MyState state = null; private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport( this); private Timer timer; private int progress = 0; public MyState getState() { return state; } public void setState(MyState state) { MyState oldValue = this.state; MyState newValue = state; this.state = newValue; pcSupport.firePropertyChange(MyState.class.getName(), oldValue, newValue); } public int getProgress() { return progress; } public void setProgress(int progress) { Integer oldValue = this.progress; Integer newValue = progress; this.progress = newValue; pcSupport.firePropertyChange(PROGRESS, oldValue, newValue); } public void play() { MyState oldState = getState(); setState(MyState.PLAY); if (oldState == MyState.PAUSE) { if (timer != null) { timer.start(); return; } } int timerDelay = 50; // simulate playing .... timer = new Timer(timerDelay, new ActionListener() { int timerProgress = 0; @Override public void actionPerformed(ActionEvent actEvt) { timerProgress++; setProgress(timerProgress); if (timerProgress >= MAX_PROGRESS) { setProgress(0); MyModel.this.stop(); } } }); timer.start(); } public void pause() { setState(MyState.PAUSE); if (timer != null && timer.isRunning()) { timer.stop(); } } public void stop() { setState(MyState.STOP); setProgress(0); if (timer != null && timer.isRunning()) { timer.stop(); } timer = null; } public void addPropertyChangeListener(PropertyChangeListener listener) { pcSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcSupport.removePropertyChangeListener(listener); } } 

请问这些是否有点混乱。

在这种情况下,我倾向于使用单身人士。 当然,这取决于您的观点的独特性。 我通常为我的“窗户”(JFrames)拥有单身人士,所以我可以通过使用getter从那些导航到我需要的任何孩子。 然而,在非常复杂的情况下,这可能不是最好的主意。