2012-02-18 3 views
0

はのは、私は、Java SwingのGUIを構築しています、と私はボタンが含まれている別のパネルを含む、パネルが含まれているフレームを持っているとしましょう。 (パネルは再利用可能であると仮定し、私は個々のクラスにそれらを作りました。)子GUIコンポーネントはどのようにして(MVCを使用して)親にアクセスする必要がありますか?

Frame → FirstPanel → SecondPanel → Button 

は、現実には、子どもたちの行はより複雑かもしれないが、私はちょうど簡単なこの例を維持したいです。私はボタンがその親要素の一つ(例えば、フレームのサイズを変更)を制御したい場合は

、必ずしも直接他の内部ではない2つのGUIクラス間の機能を実装するための最良の方法は何ですか?

getParent()メソッドを一緒にストリング化するか、またはFrameのインスタンスをその子孫まで一貫して通過させて、SecondPanelからアクセスできるようにするのは嫌いです。基本的には、私のクラスをデイジーチェーン化する必要はありません。これは、ボタンがモデルと直接ではない親コンポーネントを更新する必要があるインスタンス

ですか?次に、親にモデルの変更が通知され、それに応じて更新されます。

私は一緒にコンパイルし、私の問題を説明するために自分自身で実行する必要があります少し例を入れています。これはJFrameの別のJPanelのJPanelの2つのJButtonです。ボタンは、JFrameのサイズを制御します。

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.util.ArrayList; 
import javax.swing.BorderFactory; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class MVCExample 
{ 
    public static void main(String[] args) 
    { 
     Model model = new Model(); 

     Controller ctrl = new Controller(); 
     ctrl.registerModel(model); 

     View view = new View(ctrl); 
     view.setVisible(true); 

     model.init(); 
    } 

    /** 
    * Model class 
    */ 
    static class Model 
    { 
     private ArrayList<PropertyChangeListener> listeners = 
       new ArrayList<PropertyChangeListener>(); 

     private Dimension windowSize; 

     public Dimension getWindowSize(){ return windowSize; } 

     public void setWindowSize(Dimension windowSize) 
     { 
      if(!windowSize.equals(getWindowSize())) 
      { 
       firePropertyChangeEvent(getWindowSize(), windowSize); 
       this.windowSize = windowSize; 
      } 
     } 

     public void init() 
     { 
      setWindowSize(new Dimension(400, 400)); 
     } 

     public void addListener(PropertyChangeListener listener) 
     { 
      listeners.add(listener); 
     } 

     public void firePropertyChangeEvent(Object oldValue, Object newValue) 
     { 
      for(PropertyChangeListener listener : listeners) 
      { 
       listener.propertyChange(new PropertyChangeEvent(
         this, null, oldValue, newValue)); 
      } 
     } 
    } 

    /** 
    * Controller class 
    */ 
    static class Controller implements PropertyChangeListener 
    { 
     private Model model; 
     private View view; 

     public void registerModel(Model model) 
     { 
      this.model = model; 
      model.addListener(this); 
     } 

     public void registerView(View view) 
     { 
      this.view = view; 
     } 

     // Called from view 
     public void updateWindowSize(Dimension windowSize) 
     { 
      model.setWindowSize(windowSize); 
     } 

     // Called from model 
     public void propertyChange(PropertyChangeEvent pce) 
     { 
      view.processEvent(pce); 
     } 
    } 

    /** 
    * View classes 
    */ 
    static class View extends JFrame 
    { 
     public View(Controller ctrl) 
     { 
      super("JFrame"); 

      ctrl.registerView(this); 
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      getContentPane().add(new FirstPanel(ctrl)); 
      pack(); 
     } 

     public void processEvent(PropertyChangeEvent pce) 
     { 
      setPreferredSize((Dimension)pce.getNewValue()); 
      pack(); 
     } 
    } 

    static class FirstPanel extends JPanel 
    { 
     public FirstPanel(Controller ctrl) 
     { 
      setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createLineBorder(
        Color.RED, 2), "First Panel")); 

      add(new SecondPanel(ctrl)); 
     } 
    } 

    static class SecondPanel extends JPanel 
    { 
     private Controller controller; 
     private JButton smallButton = new JButton("400x400"); 
     private JButton largeButton = new JButton("800x800"); 

     public SecondPanel(Controller ctrl) 
     { 
      this.controller = ctrl; 
      setBorder(BorderFactory.createTitledBorder(
        BorderFactory.createLineBorder(
        Color.BLUE, 2), "Second Panel")); 

      add(smallButton); 
      add(largeButton); 

      smallButton.addActionListener(new ActionListener() 
      { 
       public void actionPerformed(ActionEvent ae) 
       { 
        controller.updateWindowSize(new Dimension(400, 400)); 
       } 
      }); 

      largeButton.addActionListener(new ActionListener() 
      { 
       public void actionPerformed(ActionEvent ae) 
       { 
        controller.updateWindowSize(new Dimension(800, 800)); 
       } 
      }); 
     } 
    } 
} 

私が気に入らないのは、フレームがイベントを受信するためにフレームを登録できるように、コントローラがJFrameに存在する必要があるということです。しかしコントローラはパネルがモデルと通信できるように、SecondPanel(112行目、131行目、143行目)まで完全に渡す必要があります。

私はここで何か非効率的なことが起こっているように感じます(そして、クラスはあまりにも緊密に結合します)。私の問題が明確でない場合は教えてください。

答えて

0

クラスをデカップリングしたままにしたい場合は、すべての要素をリンクするViewFactoryを追加できます。このような何かがうまくいくかもしれない:

static interface ViewFactory { 
    View makeView(Controller c); 
} 

static class DefaultViewFactory implements ViewFactory { 
    public View makeView(Controller c) { 
     Button b = new Button(); 
     b.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       c.updateWindowSize(new Dimension(800, 600)); 
     }); 
     Panel2 p2 = new Panel2(); 
     p2.add(b); 
     Panel1 p1 = new Panel1(); 
     p1.add(p2); 
     View v = new View(); 
     v.add(p1); 
     return v; 
    } 
} 

次に、あなたが別の場所にすべてのクラスを一緒にリンクするコードを持っており、それは独立して、あなたのコントローラとビューの実装の異なる場合があります。

HTH、スイング

1

は、コントローラとビューは、典型的には、UI委譲に属し、そしてモデルが分離されています。このビューは、モデルを表すための複雑な階層構造を構築することができ、コントローラは必要に応じてモデルをリッスンします。このコンポーネントは、2つの部分を結びつけるさまざまな簿記に使用されます。

あなたはUIとモデルを設定する場所ので、例えば、コンボボックスで、JComboBoxのです。レンダラーまたはエディタとボタンだけでなく、ポップアップやリスト - - ComboboxUIは、コンボボックスを構成するコンポーネントを組み立て、レイアウト、おそらくカスタムレンダリングを提供します。これはビューロジックです。また、これらのコンポーネントをすべてリッスンし、必要に応じてモデルを変更します。これがコントローラレベルです。モデルを変更すると、イベントによってコンポーネントにバブルが発生します。

したがって、ビューコードがコンポーネント階層全体を構築できない理由はありません。私は、モデルは、独自のプロパティを変更するボタンのアクションを提供していて、その後、ビューはそのプロパティの変更をリッスンし、ウィンドウのサイズを変更する必要があります:

class View implements PropertyChangeListener { 
    JFrame frame; 

    View(Model model) { 
     model.addPropertyChangeListener(this); 

     frame = new JFrame(); 

     List<Action> actions = model.getActions(); 

     JPanel panel = new JPanel(); 
     panel.setLayout(new GridLayout(1, actions.size())); 

     for(Action action : actions) { 
      panel.add(new JButton(action)); 
     } 

     frame.getContentPane().add(panel); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public void propertyChange(PropertyChangeEvent evt) { 
     frame.setSize((Dimension)evt.getNewValue()) 
    } 
} 

class Model { 
    List<Action> actions = new ArrayList<Action>(); 
    Dimension dimension; 

    Model() { 
     actions.add(new DimensionAction(400, 400)); 
     actions.add(new DimensionAction(800, 800)); 
    } 

    List<Action> getActions() { 
     return Collections.unmodifiableList(actions); 
    } 

    void setDimension(Dimension newDimension) { 
     Dimension oldDimension = this.dimension; 
     this.dimension = newDimension; 

     firePropertyChange("dimension", oldDimension, newDimension); 
    } 

    ... Property change support ... 

    class DimensionAction extends AbstractAction { 
     Dimension dimension; 

     DimensionAction(int width, int height) { 
      super(width + "x" + height); 
      this.dimension = new Dimension(width, height); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      Model.this.dimension = dimension; 
     } 
    } 
} 
+0

これは興味深いアプローチです。私はもっ​​と慎重に考えなければならないでしょう。 – Knave

関連する問題