2012-06-16 19 views
5

Tôi thấy rằng việc viết mã OO tốt với Swing cực kỳ khó. Vấn đề của tôi về cơ bản là tôi có một cái nhìn (một JPanel) có người nghe hành động. Người nghe hành động tìm ra nút nào đã được nhấp và gọi phương thức điều khiển thích hợp. Vấn đề là phương pháp điều khiển này cần cập nhật một chế độ xem khác. Vì vậy, vấn đề tôi gặp phải là tôi có quan điểm được truyền khắp nơi cho người kiểm soát. Đây là một ví dụ.Cách quản lý các bản cập nhật xem từ bộ điều khiển trong ứng dụng Java Swing

public class MyView extends JPanel implements ActionListener { 
    private final MyController controller = new MyController(); 

    @Override public void actionPerformed(ActionEvent e) { 
    this.controller.updateOtherView(); 
    } 
} 

Đây thực chất là những gì tôi muốn, nhưng đây là những gì sẽ xảy ra.

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

Và bạn có thể thấy rằng số lượt xem cần được cập nhật tăng và số lượng lớp trông giống như tăng này, lượt xem về cơ bản biến toàn cục và mã trở nên phức tạp và không rõ ràng. Một vấn đề khác mà tôi đang gặp phải là chế độ xem khác này thường không được chuyển trực tiếp vào MyView, nhưng nó phải trải qua các bậc cha mẹ của MyView để đến MyView, điều này thực sự chỉ gây ra lỗi cho tôi.

Ví dụ thực tế về điều này, giả sử tôi có Menu và MyView này. MyView có một nút phát, phát một số bản nhạc trong một thời gian và nó sẽ tắt (màu xám) nút phát cho đến khi nhạc kết thúc. Nếu tôi có một tùy chọn trình đơn gọi là chơi, bây giờ tôi cần phải truy cập vào nút phát lượt xem khác để tôi có thể chuyển sang màu xám. Làm cách nào tôi có thể thực hiện việc này mà không gặp phải lượt xem gây phiền nhiễu ở khắp mọi nơi? Mặc dù có thể có các giải pháp cụ thể cho vấn đề này, tôi đang tìm một thứ sẽ giải quyết vấn đề truy cập xem này trong trường hợp chung.

Tôi không chắc chắn cách khắc phục điều này. Tôi là loại sử dụng thuật ngữ mô hình MVC vào lúc này mà không cần sử dụng mẫu MVC, có thể hoặc không cần thiết. Bất kỳ trợ giúp được đánh giá cao.

+0

Vui lòng xem chỉnh sửa câu trả lời của tôi cũng như ví dụ về mã. –

Trả lời

1

Trong tình huống như vậy, tôi có xu hướng sử dụng Singletons. Tất nhiên điều này phụ thuộc vào sự độc đáo của quan điểm của bạn. Tôi thường có Singletons cho "cửa sổ" của tôi (JFrames) vì vậy tôi có thể điều hướng từ những người đến bất cứ trẻ em nào tôi cần bằng cách sử dụng getters. Tuy nhiên điều này có thể không phải là ý tưởng tốt nhất trong những tình huống rất phức tạp.

12

Một giải pháp: chỉ cần có bộ điều khiển cập nhật mô hình. Sau đó, người nghe gắn liền với mô hình sẽ cập nhật các khung nhìn. Bạn cũng có thể có JMenuItems và JButtons tương ứng chia sẻ cùng một Action, và do đó khi bạn vô hiệu hóa Action nó sẽ vô hiệu hóa tất cả các nút/menu/etc sử dụng Action đó.

Ví dụ, lớp chính:

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

} 

Các điều khiển:

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

Một Nhà nước Enum:

public enum MyState { 
    PLAY, STOP, PAUSE 
} 

Một trong những giao diện xem:

import javax.swing.Action; 

public interface MySetActions { 

    void setPlayAction(Action playAction); 
    void setPauseAction(Action pauseAction); 
    void setStopAction(Action stopAction); 
} 

Một giao diện xem:

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

Các chính Xem 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; 
    } 

} 

Phần thanh menu của Xem:

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

} 

Và cuối cùng, các mô hình:

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

Vui lòng hỏi xem có điều nào ít gây nhầm lẫn không.

+0

Triển khai của bạn @Hovercraft Full Of Eels khá ấn tượng, cảm ơn. Tôi bắt đầu tìm hiểu về mẫu MVC và tôi đang sử dụng mã của bạn làm điểm khởi đầu cho ứng dụng của tôi. Tuy nhiên, tôi muốn hỏi bạn làm thế nào bạn sẽ thực hiện một JFileChooser để mở một tập tin và nhận được đường dẫn tuyệt đối của nó "in" trên GUI. Ví dụ, tôi không biết thời gian để thực hiện JFileChooser như bạn đã làm MyMenuBar hoặc chỉ cần khai báo nó trong Model. – DaveQuinn

+0

@PMMP: có thể [ví dụ này] (http://stackoverflow.com/a/15729267/522444) (vui lòng nhấp vào liên kết) sử dụng JFileChooser có thể giúp bạn. –

Các vấn đề liên quan