MVC进度条线程化

10

我在我的设计中使用了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();
}
1个回答

12

不要调用

_progressBar.setValue(_model.getStatus());

在你的SwingWorker中不要调用Swing代码,这就是为什么需要PropertyChangeListener的原因。相反,只需设置进度属性即可。

此外,不要在doInBackground方法中调用done(),因为这需要由SwingWorker在EDT上调用。所以,让SwingWorker自己在完成时调用此方法。

另外,Done()应该写为done() ——第一个字母不应该大写,并且您应该在这个代码中使用@Override注释,以确保正确覆盖方法。

还有,这是做什么用的?

 _model.startSearch(_view.getTerm());

它是否调用需要一段时间才能完成的代码?这个应该在SwingWorker doInBackground方法内部初始化吗?

编辑: 另一个选项是给模型一个有界整型属性,例如称为进度(progress),然后直接添加一个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) {}
      }
   }
}

再次强调,doInBackground 不应包含任何与进度条或任何 Swing 组件相关的代码。无论如何,都要将其从 doInBackground 中移除。如果模型搜索代码很耗时,那么它不应该放在后台线程中吗?这就是你使用 SwingWorker 的原因。 - Hovercraft Full Of Eels
你正在锁定EDT,而使用SwingWorker的目的就是为了防止这种情况发生。请阅读教程Swing中的并发性以了解发生了什么。 - Hovercraft Full Of Eels
似乎需要更多的努力来解决这个问题。我建议您在原始帖子中创建并发布一个SSCCE,然后再发表评论。 - Hovercraft Full Of Eels
更新了。我无法上传可编译的内容,因为这是一项任务,我不能公开它。抱歉。 - Neutralise
你的_model.startSearch(getTerm())可能是阻塞的。模型本身能否返回中间结果? - Hovercraft Full Of Eels
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接