2012-02-18 2 views
0

Java Swing GUI를 만들고, 단추가 들어있는 다른 패널을 포함하는 패널이 포함 된 프레임이 있다고 가정 해 보겠습니다. (패널은 재사용 가정하고, 그래서 각각의 클래스로 그들을했습니다.)하위 GUI 구성 요소는 부모 (MVC 사용)에 어떻게 액세스해야합니까?

Frame → FirstPanel → SecondPanel → Button 

현실에서, 아이들의 라인이 더 복잡 할 수 있지만, 난 그냥 간단한 예제를 유지하려는.

단추가 상위 구성 요소 중 하나 (예 : 프레임 크기 조정)를 제어하도록하려면 두 GUI 클래스 사이에 반드시 기능을 구현하는 가장 좋은 방법은 무엇입니까?

getParent() 메서드를 함께 묶거나 Frame의 인스턴스를 자식을 통해 전달하여 SecondPanel에서 액세스 할 수있는 아이디어가 마음에 들지 않습니다. 기본적으로 데이지 체인 방식으로 수업을 함께하고 싶지 않습니다.

버튼이 부모 구성 요소가 아닌 모델을 직접 업데이트해야하는 인스턴스입니까? 그런 다음 부모에게 모델의 변경 사항이 통보되고 그에 따라 업데이트됩니다.

내 문제를 설명하기 위해 컴파일하고 실행해야하는 약간의 예를 정리했습니다. JFrame의 다른 JPanel에있는 JPanel의 두 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 위양에 속하며,이 모델은 별개이다. 뷰는 모델을 나타 내기 위해 복잡한 계층 구조의 구성 요소를 구성 할 수 있으며 컨트롤러는 필요에 따라 모델을 수신합니다. 구성 요소는 두 부분을 함께 묶는 다양한 부기에 사용됩니다.

예를 들어 콤보 상자에서 JCombobox는 UI와 모델을 설정하는 곳입니다. 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

관련 문제