MVC 进度条线程
Posted
技术标签:
【中文标题】MVC 进度条线程【英文标题】:MVC Progress Bar Threading 【发布时间】: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();
【问题讨论】:
【参考方案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();。但是状态栏没有更新。如果我上传我的新代码库,这样你就可以看到发生了什么会更容易吗?以上是关于MVC 进度条线程的主要内容,如果未能解决你的问题,请参考以下文章
如何实现Spring MVC 图片上传时,图片如何代替进度条?