【问题标题】:MVC Progress Bar ThreadingMVC 进度条线程
【发布时间】:2011-07-28 20:34:03
【问题描述】:

我在我的设计中使用 MVC 模式,当用户按下搜索按钮时,我会在模型中调用搜索,但我还想使用从该模型返回的信息更新进度条。

我尝试过使用 swingworker,但进度条没有更新。我怀疑我的线程有问题。

我在控制器中定义的按钮是:

 class SearchBtnListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            _view.displayProgress();  
        }    
}

这会调用模型中的搜索,并在视图中有以下调用:

public void displayProgress() {

    TwoWorker task = new TwoWorker();
    task.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent e) {
             if ("progress".equals(e.getPropertyName())) {
                _progressBar.setValue((Integer) e.getNewValue());
             }
         }

     });
     task.execute();             
}      


private class TwoWorker extends SwingWorker<Void, Void> {        
    @Override
    protected Void doInBackground() throws Exception {
        _model.startSearch(getTerm());                  // time intensive code
        File file = new File("lock");           
        while (file.exists()){
            setProgress(_model.getStatus());
            System.out.println(_model.getStatus()); // never called
        }           
        return null;
    }  

    protected void done(){
        updateMain();
    }
}

模型中定义的用于测试的虚拟函数:

public int getStatus(){
    Random r = new Random();
    return r.nextInt();
}

【问题讨论】:

    标签: java multithreading model-view-controller swing swingworker


    【解决方案1】:

    不要打电话

    _progressBar.setValue(_model.getStatus());
    

    从您的 SwingWorker 内部,因为这是从后台线程调用 Swing 代码,并且无论如何都是 PropertyChangeListener 的用途。相反,只需设置 progress 属性即可。

    另外,不要在 doInBackground 方法中调用 done(),因为这需要 SwingWorker 从 EDT 调用。所以让 SwingWorker 自己在实际完成时调用这个方法。

    此外,Done() 应该是 done() - 第一个字母不应该大写,并且您应该在此代码中使用 @Override 注释,这样您就可以确保您正确地覆盖了方法。

    还有,这有什么作用?

     _model.startSearch(_view.getTerm());
    

    它是否调用需要一段时间才能完成的代码?这是否应该从 SwingWorker doInBackground 本身初始化?

    编辑: 另一种选择是给模型一个绑定的 int 属性,比如称为进度,然后直接向它添加一个 PropertyChangeListener 让它更新 JProgressBar。例如,

    import java.awt.BorderLayout;
    import java.awt.event.*;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
    
    import javax.swing.*;
    
    public class MVC_ProgressBarThread {
       private static void createAndShowUI() {
          MVC_View view = new MVC_View();
          MVC_Model model = new MVC_Model();
          MVC_Control control = new MVC_Control(view, model);
          view.setControl(control);
    
          JFrame frame = new JFrame("MVC_ProgressBarThread");
          frame.getContentPane().add(view);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          java.awt.EventQueue.invokeLater(new Runnable() {
             public void run() {
                createAndShowUI();
             }
          });
       }
    }
    
    @SuppressWarnings("serial")
    class MVC_View extends JPanel {
       private MVC_Control control;
       private JProgressBar progressBar = new JProgressBar();
       private JButton startActionButton = new JButton("Start Action");
    
       public MVC_View() {
          startActionButton.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                buttonActionPerformed();
             }
          });
    
          JPanel buttonPanel = new JPanel();
          buttonPanel.add(startActionButton);
          setLayout(new BorderLayout());
          add(buttonPanel, BorderLayout.NORTH);
          add(progressBar, BorderLayout.CENTER);
       }
    
       public void setControl(MVC_Control control) {
          this.control = control;
       }
    
       private void buttonActionPerformed() {
          if (control != null) {
             control.doButtonAction();
          }
       }
    
       public void setProgress(int progress) {
          progressBar.setValue(progress);
       }
    
       public void start() {
          startActionButton.setEnabled(false);
       }
    
       public void done() {
          startActionButton.setEnabled(true);
          setProgress(100);
       }
    }
    
    class MVC_Control {
       private MVC_View view;
       private MVC_Model model;
    
       public MVC_Control(final MVC_View view, final MVC_Model model) {
          this.view = view;
          this.model = model;
          model.addPropertyChangeListener(new PropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent pce) {
                if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) {
                   view.setProgress((Integer)pce.getNewValue());
                }
             }
          });
       }
    
       public void doButtonAction() {
          view.start();
          SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {
             @Override
             protected Void doInBackground() throws Exception {
                model.reset();
                model.startSearch();
                return null;
             }
    
             @Override
             protected void done() {
                view.done();
             }
          };
          swingworker.execute();
       }
    
    }
    
    class MVC_Model {
       public static final String PROGRESS = "progress";
       private static final int MAX = 100;
       private static final long SLEEP_DELAY = 100;
       private int progress = 0;
       private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    
       public void setProgress(int progress) {
          int oldProgress = this.progress;
          this.progress = progress;
    
          PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress);
          pcs.firePropertyChange(evt);
       }
    
       public void reset() {
          setProgress(0);
       }
    
       public void addPropertyChangeListener(PropertyChangeListener listener) {
          pcs.addPropertyChangeListener(listener);
       }
    
       public void startSearch() {
          for (int i = 0; i < MAX; i++) {
             int newValue = (100 * i) / MAX;
             setProgress(newValue);
             try {
                Thread.sleep(SLEEP_DELAY);
             } catch (InterruptedException e) {}
          }
       }
    }
    

    【讨论】:

    • 我根据您的建议修改了我的代码,是的,时间繁重的代码是模型中的 startSearch(),view.getTerm() 返回用户在文本框中输入的文本。如果我把 startsearch 放到 doinbackground 过程中,我需要把进度条的绘图拿出来吗?
    • 同样,doInBackground 不应包含任何引用进度条或任何 Swing 组件的代码。不管怎样,把它从doInBackground中拿出来。是的,如果模型搜索代码时间很长,那么它不应该是属于后台线程的吗?这就是你使用 SwingWorker 的目的。
    • 我以为如果我在主线程中调用startSearch,进度条会在新线程中更新。这是我的想法,但似乎我错了。
    • 您正在锁定 EDT,这也是使用 SwingWorker 的目的,以防止这种情况发生。请阅读教程Concurrency in Swing 以了解发生了什么。
    • 看来我还为时过早!它现在确实正确绘制了状态栏,但实际状态没有更新。我在模型类中加入了一个虚拟函数: public int getStatus(){ Random r = new Random();返回 r.nextInt();}。但是状态栏没有更新。如果我上传我的新代码库,这样你就可以看到发生了什么会更容易吗?
    猜你喜欢
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 2020-10-29
    • 1970-01-01
    相关资源
    最近更新 更多