2012-06-16 15 views
5

Swingで良いOOコードを書くのは非常に難しいと思っています。私の問題は、基本的にはアクションリスナーを持つビュー(JPanel)があることです。アクションリスナーは、どのボタンがクリックされたかを把握し、適切なコントローラメソッドを呼び出します。問題は、このコントローラメソッドが別のビューを更新する必要があることです。だから、私が持っている問題は、全面的にコントローラに渡された意見があるということです。ここに例があります。Java Swingアプリケーションのコントローラからビューの更新を管理する方法

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には再生ボタンがあり、音楽をしばらく再生し、音楽が終了するまで再生ボタンを無効にします(グレーアウト)。私は、再生と呼ばれるメニューオプションがある場合、私は他のビューの再生ボタンにアクセスする必要がありますので、私はそれを灰色にすることができます。どのように私はこの邪魔にならないような意見がどこにもなくてもこれを行うことができますか?この問題の具体的な解決策があるかもしれませんが、一般的なケースではこのビューアクセスの問題を解決する方法を探しています。

この問題を解決する方法はわかりません。 MVCパターンを使用せずに現時点でMVCパターンの用語を使用していますが、これは必要な場合としない場合があります。どんな助けもありがとうございます。

+0

私の答えとコード例の編集を参照してください。 –

答えて

1

このような状況で私はシングルトンを使用する傾向があります。もちろん、これはあなたのビューの一意性に依存します。私は通常、自分の「窓」(JFrames)のためにシングルトンを持っています。そのため、ゲッターを使って必要な子供に移動できます。しかし、これは非常に複雑な状況では最良のアイデアではないかもしれません。

12

1つの解決策:コントローラーにモデルを更新させるだけです。その後、モデルにアタッチされたリスナーがビューを更新します。 JMenuItemsと対応するJButtonが同じ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<MyProgressMonitor> progMonitorList = new ArrayList<MyProgressMonitor>(); 

    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(); 
     } 
    } 
} 

A状態列挙:ビューインターフェイスの

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; 
    } 

} 

ビューのメニューバー部:

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); 
    } 
} 

これが少し混乱しているかどうか尋ねてください。

+0

あなたの実装@Hovercraft Eelsの完全な、感謝は非常に素晴らしいです。私はMVCパターンについて学び始めています。アプリケーションの出発点としてコードを使用しています。それでも、JFileChooserを実装してファイルを開き、その絶対パスをGUIに「印刷」する方法をお聞きしたいと思います。たとえば、あなたはMyMenuBarをやったようにJFileChooserを実装するか、モデルで宣言するだけではわかりません。 – DaveQuinn

+0

@PMMP:JFileChooserを使う多分[この例の](http://stackoverflow.com/a/15729267/522444)(リンクをクリックしてください)が役立ちます。 –

関連する問題